1、TCP拥塞控制算法内核实现剖析TCP拥塞控制算法内核实现剖析分类:Linux Kernel2011-12-05 17:10 内核版本:2.6.37主要源文件:linux-2.6.37/ net/ ipv4/ Tcp_cong.c一、RENO及拥塞控制算法基础1.1 RENO拥塞控制算法=struct sock *sk 和 struct tcp_sock *tp 的转换cppview plaincopy1. 在include/linux/Tcp.h中,2. staticinlinestructtcp_sock*tcp_sk(conststructsock*sk)3. 4. return(stru
2、cttcp_sock*)sk;5. 6. 7. 给出structsock*sk,8. structtcp_sock*tp=tcp_sk(sk);tcp_sock结构cppview plaincopy1. structtcp_sock2. 3. .4. u32window_clamp;/*Maximalwindowtoadvertise*/5. u32rcv_ssthresh;/*Currentwindowclamp*/6. u32rcv_wnd;/*Currentreceiverwindow*/7. .8. /*snd_wll记录发送窗口更新时,造成窗口更新的那个数据报的第一个序号。9. *它
3、主要用于在下一次判断是否需要更新发送窗口。10. */11. u32snd_wll;/*Sequenceforwindowupdate*/12. u32snd_wnd;/*发送窗口的大小,直接取值于来自对方的数据报的TCP首部*/13. /*Maximalwindoweverseenfrompeer记录来自对方通告的窗口的最大值*/14. /*Firstbytewewantanackfor发送窗口的左边沿*/15. u32max_window;u32snd_una;16. .17. /*18. *Slowstartandcongestioncontrol19. */20. u32snd_sst
4、hresh;/*Slowstartsizethreshold*/21. u32snd_cwnd;/*Sendingcongestionwindow*/22. /*表示在当前的拥塞控制窗口中已经发送的数据段的个数*/23. u32snd_cwnd_cnt;/*Linearincreasecounter*/24. u32snd_cwnd_clamp;/*Donotallowsnd_cwndtogrowabovethis*/25. .26. u32mss_cache;/*cachedeffectivemss,notincludingSACKS*/27. u32bytes_acked;/*Approp
5、riateByteCounting-RFC3465*/28. .29. 拥塞避免算法关键部分cppview plaincopy1. /*Intheorythisistp-snd_cwnd+=1/tp-snd_cwnd(oralternativew)*/2. voidtcp_cong_avoid_ai(structtcp_sock*tp,u32w)3. 4. if(tp-snd_cwnd_cnt=w)5. if(tp-snd_cwndsnd_cwnd_clamp)6. tp-snd_cwnd+;7. tp-snd_cwnd_cnt=0;8. else9. tp-snd_cwnd_cnt+;10.
6、 11. 12. EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai);慢启动算法tcp_max_ssthresh 参数的含义tcp_max_ssthresh 参数是实现RFC3742时引入的,离现在已经有4年的时间了。07年5月内核实现了这个RFC。但是,tcp的这个参数在内核文档(ip-sysctl.txt)中找不到任何的说明。慢启动阶段,就是当前拥塞窗口值比慢启动阈值(snd_ssthresh)小的时候,所处的阶段就叫做慢启动阶段。当我们收到一个新的ACK时,则会调用tcp_slow_start()这个函数,并且为拥塞窗口增加1.(Linux中拥塞窗口的值代表数据包的
7、个数,而不是实际的发送字节数目。实际可以发送的字节数等于可以发送的数据包个数*MSS。)直到慢启动阶段出现数据包的丢失。而引入了tcp_max_ssthresh这个参数后,则可以控制在慢启动阶段拥塞窗口增加的频度。默认这个参数不打开,如果这个参数的值设置为1000,则当拥塞窗口值大于1000时,则没收到一个ACK,并不再增加拥塞窗口一个单位了,而是约收到2个ACK才增加一个窗口单位。注意:收到2ACK并不是决定值!需要根据当前的拥塞窗口值,tcp_max_ssthresh值进行判断。参见tcp.txt(documentation/networking)The following variabl
8、es are used in the tcp_sock for congestion control: snd_cwnd The size of the congestion windowsnd_ssthresh Slow start threshold. We are in slow start if snd_cwnd is less than this. snd_cwnd_cnt A counter used to slow down the rate of increase once we exceed slow start threshold.snd_cwnd_clamp This i
9、s the maximum size that snd_cwnd can grow to. snd_cwnd_stamp Timestamp for when congestion window last validated. snd_cwnd_used Used as a highwater mark for how much of the congestion window is in use. It is used to adjust snd_cwnd down when the link is limited by the application rather than the net
10、work.1. voidtcp_slow_start(structtcp_sock*tp)2. 3. intcnt;/*increaseinpackets*/4. 5. /*RFC3465:ABCslowstart6. *IncreaseonlyafterafullMSSofbytesisacked7. *8. *TCPsenderSHOULDincreasecwndbythenumberof9. *previouslyunacknowledgedbytesACKedbyeachincoming10. *acknowledgment,providedtheincreaseisnotmoreth
11、anL11. */12. /*ack的数据少于MSS,tcp_abc默认关闭*/13. if(sysctl_tcp_abc&tp-bytes_ackedmss_cached)14. return;15. 16. /*当前拥塞窗口值大于sysctl_tcp_max_ssthresh. 限制拥塞窗口增长值 */17. if(sysctl_tcp_max_ssthresh0&tcp-snd_cwndsysctl_tcp_max_ssthresh)18. cnt=sysctl_tcp_max_ssthresh1;/*limitedslowstart*/19. else20. cnt=tp-snd_cw
12、nd;/*exponentialincrease*/21. 22. /*RFC3465:ABC23. *WeMAYincreaseby2ifdiscovereddelayedack24. */25. /*如果接收方启用了延时确认,此时收到的确认代表两个MSS数据报*/26. if(sysctl_tcp_abc1&tp-bytes_acked=2*tp-mss_cache)27. cntbytes_acked=0;30. tp-snd_cwnd_cnt+=cnt;/*此时snd_cwnd_cnt等于snd_cwnd或2*snd_cwnd*/31. 32. /* 最多增加1/2 sysctl_tc
13、p_max_ssthresh字节。也就是说,至少2个ACK才能让拥塞窗口增加1. */33. while(tp-snd_cwnd_cnt=tp-snd_cwnd)34. tp-snd_cwnd_cnt-=tp-snd_cwnd;35. if(tp-snd_cwndsnd_cwnd_clamp)36. tp-snd_cwnd+;37. 38. 39. EXPORT_SYMBOL_GPL(tcp_slow_start);1.2 拥塞控制算法基础1.2.1 代表拥塞算法的结构体1. #defineTCP_CA_NAME_MAX162. structtcp_congestion_ops3. struc
14、tlist_headlist;4. unsignedlongflags;5. /*initializeprivatedata(optional)*/6. void(*init)(structsock*sk);7. /*cleanupprivatedata(optional)*/8. void(*release)(structsock*sk);9. /*returnslowstartthreshold(required)*/10. u32(*ssthresh)(structsock*sk);11. /*lowerboundforcongestionwindow(optional)*/12. u3
15、2(*min_cwnd)(conststructsock*sk);13. /*donewcwndcalculation(required)*/14. void(*cong_avoid)(structsock*sk,u32ack,u32in_flight);15. /*callbeforechangingca_state(optional)*/16. void(*set_state)(structsock*sk,u8new_state);17. /*callwhencwndeventoccurs(optional)*/18. void(*cwnd_event)(structsock*sk,enu
16、mtcp_ca_eventev);19. /*newvalueofcwndafterloss(optional)*/20. u32(*undo_cwnd)(structsock*sk);21. /*hookforpacketackaccounting(optional)*/22. void(*pkts_acked)(structsock*sk,u32num_acked,s32rtt_us);23. /*getinfoforinet_diag(optional)*/24. void(*get_info)(structsock*sk,u32ext,structsk_buff*skb);25. ch
17、arnameTCP_CA_NAME_MAX;26. structmodule*owner;27. 在Tcp_cong.c中,有全局变量:int sysctl_tcp_max_ssthresh = 0 ;/* define DEFINE_SPINLOCK(x) spinlock_t x = _SPIN_LOCK_UNLOCKED(x) */static DEFINE_SPINLOCK( tcp_cong_list_lock ) ;static LIST_HEAD( tcp_cong_list ) ; / tcp拥塞控制算法链表,其元素为tcp_congestion_ops/*BUG_ON( )
18、; 如果BUG_ON中的条件为真就调用BUG,它输出一些信息,然后调用panic函数挂起系统。char *strncpy( char * dest , char *src , size_t n ) ;它与strcpy不同之处在于复制n个字符,而不是把所有的字符拷贝(包括结尾0)。当src的长度小于n时,dst内的未复制空间用0填充。否则,复制n个字符到dst,没有加0。这里就要注意在字符串dst结尾处理加0的情况了。rcu_read_lock() / 读者在读取由RCU保护的共享数据时使用该函数标记它进入读端临界区。rcu_read_unlock() / 该函数与rcu_read_lock配对
19、使用,用以标记读者退出读端临界区。*/1.2.2 对拥塞控制算法的一些操作(读写增减注册等)cppview plaincopy1. /*Getcurrentdefaultcongestioncontrol*/2. voidtcp_get_default_congestion_control(char*name)3. 4. structtcp_congestion_ops*ca;5. /*Wewillalwayshavereno*/6. BUG_ON(list_empty(&tcp_cong_list);7. 8. rcu_read_lock();9. ca=list_entry(tcp_con
20、g_list.next,structtcp_congestion_ops,list);10. strncpy(name,ca-name,TCP_CA_NAME_MAX);11. rcu_read_unlock();12. struct sockrepresentation of socketsstruct inet_sockrepresentation of INET socketsstruct inet_connection_sockINET connection oriented socketsstruct tcp_socktcp sockets以上几种socket越分越细,比如inet_
21、connection_sock是在inet_sock上的扩展,具有自己特有的属性。tcp_sock是TCP协议专用的一个socket表示,它是在struct inet_connection_sock基础进行扩展,主要是增加了滑动窗口协议,避免拥塞算法等一些TCP专有属性。cppview plaincopy1. structinet_connection_sock2. .3. /Pluggablecongestioncontrolhook4. conststructtcp_congestion_ops*icsk_ca_ops;5. .6. 7. u32icsk_ca_priv16;8. #def
22、ineICSK_CA_PRIV_SIZE(16*sizeof(u32)9. 举例:/有一个初始化了得struct sock *skstruct inet_connection_sock *icsk = inet_csk( sk ) ;printk(KERN_INFO %s , icsk-icsk_ca_ops-name) ; /当前连接拥塞控制算法名称1. structinet_sock2. .3. /*Socketdemultiplexcomparisonsonincomingpackets*/4. _be32inet_daddr;5. _be16inet_dport;6. _be32ine
23、t_saddr;7. _be16inet_sport;8. _be16inet_num;/localport9. _be32inet_rcv_saddr;/BoundlocalIPv4addr10. .11. 1. /*Builtlistofnon-restrictedcongestioncontrolvalues*/2. voidtcp_get_allowed_congestion_control(char*buf,size_tmaxlen)3. 4. structtcp_congestion_ops*ca;5. size_toffs=0;6. *buf=0;/有必要?7. rcu_read
24、_lock();8. list_for_each_entry(ca,&tcp_cong_list,list)9. if(!(ca-flags&TCP_CONG_NON_RESTRICTED)/排除有限制的。限制和非限制区别?10. continue;11. offs+=snprintf(buf+offs,maxlen-offs,%s%s,offs=0?:,ca-name);12. 13. rcu_read_unlock();14. 1. /*Simplelinearsearch,dontexpectmanyentries!*/2. staticstructtcp_congestion_ops*tcp_ca_find(constchar*name)3. 4. structtcp_congestion_ops*e;5. list_for_each_entry_rcu(e,&tcp_cong_list,list)6. if(strcmp(e-name,name)=0)7. returne;8. 9. returnNULL;10. 1. /*2. *Attachnewcongestioncontrolalgorith
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1