TCP拥塞控制算法内核实现剖析(一)

来源:互联网 发布:淘宝负面评价怎么办 编辑:程序博客网 时间:2024/04/29 05:49
内核版本:2.6.37

主要源文件:linux-2.6.37/ net/ ipv4/ Tcp_cong.c 

本文主要分析RENO及TCP拥塞控制基础的实现

======================================================================================================

struct sock *sk 和 struct tcp_sock *tp 的转换

在include/ linux/ Tcp.h中,static inline struct tcp_sock *tcp_sk(const struct sock *sk) {        return (struct tcp_sock *)sk ;}给出struct sock *sk,struct tcp_sock *tp = tcp_sk(sk) ;

 

tcp_sock结构

struct tcp_sock{       ... u32 window_clamp ; /* Maximal window to advertise */ u32 rcv_ssthresh ; /* Current window clamp */ u32 rcv_wnd ; /* Current receiver window */       ... /* snd_wll 记录发送窗口更新时,造成窗口更新的那个数据报的第一个序号。  * 它主要用于在下一次判断是否需要更新发送窗口。  */ u32 snd_wll ; /* Sequence for window update */  u32 snd_wnd ; /* 发送窗口的大小,直接取值于来自对方的数据报的TCP首部 */ /* Maximal window ever seen from peer 记录来自对方通告的窗口的最大值 */ /* First byte we want an ack for 发送窗口的左边沿 */ u32 max_window ;  u32 snd_una ;         ... /*  * Slow start and congestion control  */ u32 snd_ssthresh ; /* Slow start size threshold */ u32 snd_cwnd ; /* Sending congestion window */ /*表示在当前的拥塞控制窗口中已经发送的数据段的个数*/ u32 snd_cwnd_cnt ; /* Linear increase counter */  u32 snd_cwnd_clamp ; /* Do not allow snd_cwnd to grow above this */        ... u32 mss_cache ; /* cached effective mss , not including SACKS */ u32 bytes_acked ; /* Appropriate Byte Counting - RFC3465 */       ...}


拥塞避免算法关键部分

/* In theory this is tp->snd_cwnd += 1 / tp->snd_cwnd ( or alternative w ) */void tcp_cong_avoid_ai(struct tcp_sock *tp , u32 w){        if ( tp->snd_cwnd_cnt >= w) {                   if ( tp->snd_cwnd < tp->snd_cwnd_clamp)                        tp->snd_cwnd++ ;                   tp->snd_cwnd_cnt = 0 ;        } else {                   tp->snd_cwnd_cnt ++ ;        }                }EXPORT_SYMBOL_GPL(tcp_cong_avoid_ai) ;


慢启动算法

void tcp_slow_start( struct tcp_sock *tp ){ int cnt ; /* increase in packets *//* RFC3465 : ABC slow start * Increase only after a full MSS of bytes is acked * * TCP sender SHOULD increase cwnd by the number of * previously unacknowledged bytes ACKed by each incoming  * acknowledgment , provided the increase is not more than L */ /* ack的数据少于MSS */ if ( sysctl_tcp_abc && tp->bytes_acked < tp->mss_cached )          return ;/* 此时不是应该进入拥塞避免?*/ if ( sysctl_tcp_max_ssthresh >0 && tcp->snd_cwnd >sysctl_tcp_max_ssthresh)        cnt = sysctl_tcp_max_ssthresh >> 1 ; /* limited slow start */ else         cnt = tp->snd_cwnd ; /* exponential increase *//* RFC3465 : ABC * We MAY increase by 2 if discovered delayed ack   *//* 如果接收方启用了延时确认,此时收到的确认代表两个MSS数据报*/if ( sysctl_tcp_abc >1 && tp->bytes_acked >= 2*tp->mss_cache )         cnt <<= 1 ;tp->bytes_acked = 0 ; tp->snd_cwnd_cnt += cnt ; /* 此时snd_cwnd_cnt等于snd_cwnd或2*snd_cwnd */ while( tp->snd_cwnd_cnt >= tp->snd_cwnd ) {         tp->snd_cwnd_cnt -= tp->snd_cwnd ;        if( tp->snd_cwnd < tp->snd_cwnd_clamp )                tp->snd_cwnd++ ; }}EXPORT_SYMBOL_GPL( tcp_slow_start ) ;


代表拥塞算法的结构体

#define TCP_CA_NAME_MAX 16struct tcp_congestion_ops {        struct list_head list ;        unsigned long flags ;        /* initialize private data (optional) */        void (*init) (struct sock *sk) ;        /* cleanup private data (optional) */         void (*release) (struct sock *sk) ;        /* return slow start threshold (required) */        u32 (*ssthresh) (struct sock *sk) ;        /* lower bound for congestion window (optional) */        u32 (*min_cwnd) (const struct sock *sk) ;        /* do new cwnd calculation (required) */        void (*cong_avoid) (struct sock *sk , u32 ack , u32 in_flight ) ;        /* call before changing ca_state (optional) */        void (*set_state) (struct sock *sk , u8 new_state) ;        /* call when cwnd event occurs (optional) */        void (*cwnd_event) (struct sock *sk , enum tcp_ca_event ev) ;        /* new value of cwnd after loss (optional) */        u32 (*undo_cwnd) (struct sock *sk) ;        /* hook for packet ack accounting (optional) */        void (*pkts_acked) (struct sock *sk , u32 num_acked , s32 rtt_us) ;        /* get info for inet_diag (optional) */        void (*get_info) (struct sock *sk , u32 ext , struct sk_buff *skb) ;        char name[TCP_CA_NAME_MAX] ;        struct module *owner ;}

 

在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( ) ; 如果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配对使用,用以标记读者退出读端临界区。

*/

 

对拥塞控制算法的一些操作(读写增减注册等)

/* Get current default congestion control */void tcp_get_default_congestion_control( char *name ){        struct tcp_congestion_ops *ca ;        /* We will always have reno */        BUG_ON( list_empty( &tcp_cong_list) ) ;        rcu_read_lock( ) ;        ca = list_entry( tcp_cong_list . next , struct tcp_congestion_ops , list ) ;        strncpy( name , ca->name , TCP_CA_NAME_MAX ) ;        rcu_read_unlock( ) ;}

 

struct sock——representation of sockets

 struct inet_sock——representation of INET sockets

 struct inet_connection_sock——INET connection oriented sockets

 struct tcp_sock——tcp sockets

 以上几种socket越分越细,比如inet_connection_sock是在inet_sock上的扩展,具有自己特有的属性。

 tcp_sock是TCP协议专用的一个socket表示,它是在struct inet_connection_sock基础进行扩展,主要是增加了滑动窗口协议,避免拥塞算法等一些TCP专有属性。

 

struct inet_connection_sock {        ...        // Pluggable congestion control hook        const struct tcp_congestion_ops *icsk_ca_ops ;         ...        u32 icsk_ca_priv[16] ;#define ICSK_CA_PRIV_SIZE (16*sizeof(u32))}

 

举例://有一个初始化了得struct sock *sk

struct inet_connection_sock *icsk = inet_csk( sk ) ;

printk(KERN_INFO "%s" , icsk->icsk_ca_ops->name) ; //当前连接拥塞控制算法名称

 

struct inet_sock {        ...        /* Socket demultiplex comparisons on incoming packets */        __be32 inet_daddr ;        __be16 inet_dport ;        __be32 inet_saddr ;        __be16 inet_sport ;        __be16 inet_num ; // local port         __be32 inet_rcv_saddr ; // Bound local IPv4 addr        ...}


 

/* Built list of non-restricted congestion control values*/void tcp_get_allowed_congestion_control( char *buf , size_t maxlen){        struct tcp_congestion_ops *ca ;        size_t offs = 0 ;        *buf = '\0' ; //有必要?         rcu_read_lock() ;        list_for_each_entry( ca , &tcp_cong_list , list ) {                if( !( ca->flags & TCP_CONG_NON_RESTRICTED)) //排除有限制的。限制和非限制区别?                         continue;                offs += snprintf( buf+offs , maxlen-offs , "%s%s" , offs == 0?"" : " " , ca->name) ;        }        rcu_read_unlock() ;}


 

/* Simple linear search , don't expect many entries! */static struct tcp_congestion_ops*tcp_ca_find( const char *name){        struct tcp_congestion_ops *e ;        list_for_each_entry_rcu( e , &tcp_cong_list , list ) {                if( strcmp(e->name , name)==0)                        return e ;         }         return NULL ;}


 

/* * Attach new congestion control algorithm to the list  * of available options. */int tcp_register_congestion_control( struct tcp_congestion_ops *ca ){        int ret = 0 ;        /* all algorithms must implement ssthresh and cong_avoid ops */        if ( !ca->ssthresh || !ca->cong_avoid ) {                printk(KERN_ERR "TCP %s does not implement required ops\n",                            ca->name) ;                return -EINVAL ;        }        spin_lock(&tcp_cong_list_lock) ;        if( tcp_ca_find (ca->name)) {                printk(KERN_NOTICE "TCP %s already registered\n", ca->name) ;                ret = -EEXIST; //不能直接return,不然会造成死锁         } else {                list_add_tail_rcu( &ca->list , &tcp_cong_list) ;                printk(KERN_INFO "TCP %s registered\n", ca->name) ;        }        spin_unlock(&tcp_cong_list_lock) ;        return ret ;}


======================================================================================================

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 康佳电视打开右下角是红色的怎么办 天猫超市退款成功后收到货怎么办 天猫还没收到货就确认收货了怎么办 支付宝红包抵扣被关了怎么办 水貂绒大衣白色过色了怎么办 双面羊绒大衣袖子洗短了怎么办 特殊类型订单销量评价删除后怎么办 淘宝上卖家发货发错了地址怎么办 买完保险想换保险代理人怎么办 怀孕内裤两边磨的好疼怎么办 露肩连体裤穿着卡裆怎么办 魅族手机恋与制作人换诺基亚怎么办 蚊子叮咬后擦风油精了红肿怎么办 机动车已转让没过户出了事情怎么办 签好的合同如果甲方违约怎么办? 饭店没签合同辞职不给工资怎么办 两家为了带孩子闹翻了怎么办啊? 抵押后租赁的房屋被法院拍卖怎么办 房子买20年了没有过户怎么办 二手车没过户行驶证丢了怎么办 买的二手车行驶证丢了怎么办 在京东仓库做事把东西损坏了怎么办 微信显示该账号登陆环境异常怎么办 干洗店把衣服洗坏了怎么办 千牛上怎么改淘宝密码忘记了怎么办 公司收了代理商押金到期不退怎么办 淘宝显示签收但是我没收到货怎么办 淘宝东西没收到显示签收了怎么办 开拼多多店铺的密码忘了怎么办 拼多多密码跟店铺名忘了怎么办 闲鱼上卖出的宝贝被调包了怎么办 上传身份证照片说格式错误该怎么办 我给厂里打的款不给发货怎么办 净值接近不定期份额折算阀值怎么办 有锁电信4g掉了怎么办 在电脑中找不到想作废的发票怎么办 科目三补考费发票丢了怎么办 母婴店飞鹤奶粉突然厂家撤货怎么办 澳门买的保健品感觉是假的怎么办 淘宝买东西提交需求时卡死了怎么办 天猫精灵显示为离线状态怎么办