Chinaunix首页 | 论坛 | 博客
  • 博客访问: 145755
  • 博文数量: 24
  • 博客积分: 791
  • 博客等级: 军士长
  • 技术积分: 350
  • 用 户 组: 普通用户
  • 注册时间: 2010-02-22 11:00
文章存档

2011年(18)

2010年(6)

分类: LINUX

2011-04-28 12:50:17

在分析三次握手的流程时,我们知道当收到ack或synack时都要调用下面这个函数:
  1. int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb,
  2.              struct tcphdr *th, unsigned len)
  3. {
  4.     ......
  5.     switch (sk->sk_state) {

  6.     case TCP_SYN_SENT://主机发送syn包后,状态为TCP_SYN_SENT;当我们收到synack时,就会调用
  7.                         tcp_rcv_synsent_state_process函数
  8.         queued = tcp_rcv_synsent_state_process(sk, skb, th, len);//函数会调用
  9.                                         tcp_init_congestion_control进行拥塞控制初始化
  10.     ......
  11.     }

  12.     ......
  13.     /* step 5: check the ACK field */
  14.     if (th->ack) {//主机发送synack包后,收到的ack或者为第一个数据包的ack
  15.         int acceptable = tcp_ack(sk, skb, FLAG_SLOWPATH) > 0;

  16.         switch (sk->sk_state) {
  17.         case TCP_SYN_RECV://主机发送synack后,状态变为TCP_SYN_RCV,至于为什么没有立即变为
  18.                             ESTABLISHED,以后再说
  19.                 ......
  20.                 tcp_init_congestion_control(sk);//设置拥塞控制机制
  21.                 ......
  22.             break;
  23.         ......
  24.     } else
  25.         goto discard;
  26.     ......
  27. }
分析tcp_init_congestion_control函数:
  1. void tcp_init_congestion_control(struct sock *sk)
  2. {
  3.     struct inet_connection_sock *icsk = inet_csk(sk);
  4.     struct tcp_congestion_ops *ca;

  5.     /* if no choice made yet assign the current value set as default */
  6.     if (icsk->icsk_ca_ops == &tcp_init_congestion_ops) {//在tcp_v4_init_sock函数中,我们
  7. icsk->icsk_ca_ops = &tcp_init_congestion_ops
  8.         rcu_read_lock();
  9.         list_for_each_entry_rcu(ca, &tcp_cong_list, list) {//遍历查找第一个active的拥塞模
  10.                                                              块,并设置为连接的拥塞控制
  11.             if (try_module_get(ca->owner)) {//模块存在并且模块是live(不为
  12.                                               MODULE_STATE_GOING)
  13.                 icsk->icsk_ca_ops = ca;
  14.                 break;
  15.             }

  16.             /* fallback to next available */
  17.         }
  18.         rcu_read_unlock();
  19.     }

  20.     if (icsk->icsk_ca_ops->init)
  21.         icsk->icsk_ca_ops->init(sk);//拥塞控制钩子中的init函数非空,就调用init进行初始化;连接建立时才会调用这个函数,所以如果要在连接刚建立时进行操作,可以在init函数中添加你的代码,毕竟拥塞控制是模块插入的,不必修改内核
  22. }

内核是在tcp_cong_list中查找第一个active的拥塞控制模块并设置为当前连接的拥塞控制策略,在拥塞模块注册的时候是将模块插到链表的末尾;如果要使它起作用,必须设置

sysctl -w net.ipv4.tcp_congestion_control=(your congestion control ops name)

这个设置的作用是在你的拥塞控制放到链表的第一个节点位置上;早期版本的内核,如2.6.18,在注册时是将模块插到链表的头部,所以不用设置就会在下个连接建立时被设置。

在sysctl_net_ipv4.c中

  1. {
  2.         .procname    = "tcp_congestion_control",
  3.         .mode        = 0644,
  4.         .maxlen        = TCP_CA_NAME_MAX,
  5.         .proc_handler    = proc_tcp_congestion_control,//procfs提供的回调函数
  6.     },

当我们通过sysctl -w操作时,就会调用回调函数:

  1. static int proc_tcp_congestion_control(ctl_table *ctl, int write,
  2.                  void __user *buffer, size_t *lenp, loff_t *ppos)
  3. {
  4.     char val[TCP_CA_NAME_MAX];
  5.     ctl_table tbl = {
  6.         .data = val,//拥塞模块的名字
  7.         .maxlen = TCP_CA_NAME_MAX,//拥塞模块的名字最长大小
  8.     };
  9.     int ret;

  10.     tcp_get_default_congestion_control(val);//获得当前使用的拥塞控制,及链表的首节点

  11.     ret = proc_dostring(&tbl, write, buffer, lenp, ppos);//将用户层buffer里面的东西拷贝到
  12.                                                            tbl.data,buffer里面保存着我们
  13.                                                            通过sysctl -w设定的字符串
  14.     if (write && ret == 0)
  15.         ret = tcp_set_default_congestion_control(val);//将我们设定的拥塞控制放到链表的首位置
  16.     return ret;
  17. }
  1. int tcp_set_default_congestion_control(const char *name)
  2. {
  3.     struct tcp_congestion_ops *ca;
  4.     int ret = -ENOENT;

  5.     spin_lock(&tcp_cong_list_lock);
  6.     ca = tcp_ca_find(name);//查找链表中是否已经存在将操作的拥塞控制模块
  7. #ifdef CONFIG_MODULES
  8.     if (!ca && capable(CAP_NET_ADMIN)) {
  9.         spin_unlock(&tcp_cong_list_lock);

  10.         request_module("tcp_%s", name);
  11.         spin_lock(&tcp_cong_list_lock);
  12.         ca = tcp_ca_find(name);
  13.     }
  14. #endif

  15.     if (ca) {//模块存在
  16.         ca->flags |= TCP_CONG_NON_RESTRICTED;    /* default is always allowed */
  17.         list_move(&ca->list, &tcp_cong_list);//将需要设置的模块插入链表的首位置
  18.         ret = 0;
  19.     }
  20.     spin_unlock(&tcp_cong_list_lock);

  21.     return ret;
  22. }
关于sysctl及procfs以后再分析
阅读(3019) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~