TCP/IP源码学习(24)——tcp_write_xmit

原文地址:TCP/IP源码学习(24)——tcp_write_xmit 作者:GFree_Wind

本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以*拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
    

昨天把tcp_sendmsg学习完毕,今天开始学习__tcp_push_pending_frames。该函数将所有pending的数据,全部发送出去
  1. void __tcp_push_pending_frames(struct sock *sk, unsigned int cur_mss,
  2.              int nonagle)
  3. {
  4.     /* If we are closed, the bytes will have to remain here.
  5.      * In time closedown will finish, we empty the write queue and
  6.      * all will be happy.
  7.      */
  8.     /* 该socket已经关闭,那么直接返回 */
  9.     if (unlikely(sk->sk_state == TCP_CLOSE))
  10.         return;
     /* 发送数据 */
  1.     if (tcp_write_xmit(sk, cur_mss, nonagle, 0, GFP_ATOMIC))
  2.         tcp_check_probe_timer(sk); //发送数据失败,使用probe timer进行检查。
  3. }
下面看函数tcp_write_xmit
  1. static int tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
  2.              int push_one, gfp_t gfp)
  3. {
  4.     struct tcp_sock *tp = tcp_sk(sk);
  5.     struct sk_buff *skb;
  6.     unsigned int tso_segs, sent_pkts;
  7.     int cwnd_quota;
  8.     int result;

  9.     sent_pkts = 0;
      /* 检查是不是只发送一个skb buffer,即push one */
  1.     if (!push_one) {
  2.         /* 
  3.         如果要发送多个skb,则需要检测MTU。
  4.         这时会检测MTU,希望MTU可以比之前的大,提高发送效率。
  5.         */
  6.         /* Do MTU probing. */
  7.         result = tcp_mtu_probe(sk);
  8.         if (!result) {
  9.             return 0;
  10.         } else if (result > 0) {
  11.             sent_pkts = 1;
  12.         }
  13.     }
     
     /* 检查是否还有skb要发送 */
  1.     while ((skb = tcp_send_head(sk))) {
  2.         unsigned int limit;
         /* 
         初始化TSO
         关于TSO,参考:http://en.wikipedia.org/wiki/Large_segment_offload
         */
  1.         tso_segs = tcp_init_tso_segs(sk, skb, mss_now);
  2.         BUG_ON(!tso_segs);
          /* 检查congestion windows, 可以发送几个segment */
  1.         cwnd_quota = tcp_cwnd_test(tp, skb);
  2.         if (!cwnd_quota)
  3.             break;
         /* 检查发送窗口,是否可以容纳至少一个segment */
  1.         if (unlikely(!tcp_snd_wnd_test(tp, skb, mss_now)))
  2.             break;

  3.         if (tso_segs == 1) {
  4.             /* 根据nagle算法,计算是否需要发送数据 */
  5.             if (unlikely(!tcp_nagle_test(tp, skb, mss_now,
  6.                          (tcp_skb_is_last(sk, skb) ?
  7.                          nonagle : TCP_NAGLE_PUSH))))
  8.                 break;
  9.         } else {
  10.             /* 当不止一个skb时,通过TSO计算是否需要延时发送 */
  11.             if (!push_one && tcp_tso_should_defer(sk, skb))
  12.                 break;
  13.         }

  14.         limit = mss_now;
  15.         /* 在TSO分片大于1的情况下,且TCP不是URG模式。通过MSS计算发送数据的limit */
  16.         if (tso_segs > 1 && !tcp_urg_mode(tp))
  17.             limit = tcp_mss_split_point(sk, skb, mss_now,
  18.                          cwnd_quota);
         /* 当skb的长度大于限制时,需要调用tso_fragment分片 */
  1.         if (skb->len > limit &&
  2.          unlikely(tso_fragment(sk, skb, limit, mss_now, gfp)))
  3.             break;
         /* 更新tcp的时间戳 */
  1.         TCP_SKB_CB(skb)->when = tcp_time_stamp;
         /* 发送skb数据 */
  1.         if (unlikely(tcp_transmit_skb(sk, skb, 1, gfp)))
  2.             break;

  3.         /* Advance the send_head. This one is sent out.
  4.          * This call will increment packets_out.
  5.          */
  6.         /* 更新统计,并启动重传计时器 */
  7.         tcp_event_new_data_sent(sk, skb);

  8.         tcp_minshall_update(tp, mss_now, skb);
  9.         sent_pkts++;

  10.         if (push_one)
  11.             break;
  12.     }

  13.     if (likely(sent_pkts)) {
  14.         tcp_cwnd_validate(sk);
  15.         return 0;
  16.     }
  17.     return !tp->packets_out && tcp_send_head(sk);
  18. }
今天就学到这里。发现TCP的内部机制的复杂度,远胜于UDP。不仅仅是TCP协议所表现的那些,内部机制更为复杂。所以我可能会先再看看资料,把基础打牢,在继续学习tcp/ip的源码了。
上一篇:【技术贴】修改双系统Windows引导菜单里面的文字。。。


下一篇:微软开始修正Outlook同步异常问题 未透露故障原因