TCP/IP源码学习(23)——tcp_sendmsg(2)

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

继续前面的学习,tcp_sendmsg
  1. int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
  2.         size_t size)
  3. {
  4.     /* 
  5.     省略之前的代码
  6.     */
  7.     while (--iovlen >= 0) {
  8.         size_t seglen = iov->iov_len;
  9.         unsigned char __user *from = iov->iov_base;

  10.         iov++;

  11.         while (seglen > 0) {
    1.           /* 
    2.           省略了之前的代码
    3.           */           
  12.  
  13.             /*
  14.             调整要复制的字节数,使最多只能复制剩下的字节数seglen。
  15.             从后面可以看出,seglen逐渐减少
  16.             */
  17.             /* Try to append data to the end of skb. */
  18.             if (copy > seglen)
  19.                 copy = seglen;

  20.             /* Where to copy to? */
  21.             if (skb_tailroom(skb) > 0) {
  22.                 /* 如果skb还有空间,则复制到skb中去*/
  23.                 /* We have some space in skb head. */
  24.                 /*
  25.                 调整copy的大小,使其不得大于tailroom的空间。
  26.                 */
  27.                 if (copy > skb_tailroom(skb))
  28.                     copy = skb_tailroom(skb);
  29.                 /* 将数据加到skb中去*/
  30.                 if ((err = skb_add_data(skb, from, copy)) != 0)
  31.                     goto do_fault;
  32.             } else {
  33.                 /* skb中没有空闲空间了 */
  34.                 
  35.                 /*
  36.                 这时的数据组织方式,可以参考我的另外一篇博文《tcp/ip源码(19)——Scatter/Gather I                 /O在L3中的应用
  37.                 */
  38.                 int merge = 0;
  39.                 int i = skb_shinfo(skb)->nr_frags;
  40.                 struct page *page = TCP_PAGE(sk);
  41.                 int off = TCP_OFF(sk);

  42.                 if (skb_can_coalesce(skb, i, page, off) &&
  43.                  off != PAGE_SIZE) {
  44.                     /* We can extend the last page
  45.                      * fragment. */
  46.                     /* 可以将数据加到最后一个page中 */
  47.                     merge = 1;
  48.                 } else if (i == MAX_SKB_FRAGS || !sg) {
  49.                     /* Need to add new fragment and cannot
  50.                      * do this because interface is non-SG,
  51.                      * or because all the page slots are
  52.                      * busy. */
  53.                     /* 
  54.                     到达此处,表明当前的page无法填充数据。
  55.                     这时,数据分片达到最大的frag数量,或者不支持Scatter Gather功能。那么都无法继续                     填充。这时,将tcp置为push标志,尽快发送数据
  56.                     */
  57.                     tcp_mark_push(tp, skb);
  58.                     /* 需要申请一个新的skb */
  59.                     goto new_segment;
  60.                 } else if (page) {
  61.                     if (off == PAGE_SIZE) {
  62.                         /* 
  63.                         之前的page已经写满.所以该page已经不能再继续填充了,
  64.                         因此将sk->sk_sndmsg_page和page置为null
  65.                         */
  1.                         put_page(page);
  2.                         TCP_PAGE(sk) = page = NULL;
  3.                         off = 0;
  4.                     }
  5.                 } else 
  6.                     off = 0; //没有可用page,所以offset为0
                  /* 调整copy的大小,使之不得大于page剩下的空间 */
  1.                 if (copy > PAGE_SIZE - off)
  2.                     copy = PAGE_SIZE - off;
                  /* 判断是否需要等待,直到有许可的内存使用 */
  1.                 if (!sk_wmem_schedule(sk, copy))
  2.                     goto wait_for_memory;

  3.                 if (!page) {
  4.                     /* 如当前没有page,则申请一个新的page */
  5.                     /* Allocate new cache page. */
  6.                     if (!(page = sk_stream_alloc_page(sk)))
  7.                         goto wait_for_memory;
  8.                 }
                 /* 复制数据到当前page */
  1.                 /* Time to copy data. We are close to
  2.                  * the */
  3.                 err = skb_copy_to_page(sk, from, skb, page,
  4.                          off, copy);
  5.                 if (err) {
  6.                     /* If this page was new, give it to the
  7.                      * socket so it does not get leaked.
  8.                      */
  9.                     /* 出错了。则把这个page交给该socket,所以没有内存泄露 */
  10.                     if (!TCP_PAGE(sk)) {
  11.                         TCP_PAGE(sk) = page;
  12.                         TCP_OFF(sk) = 0;
  13.                     }
  14.                     goto do_error;
  15.                 }

  16.                 /* Update the skb. */
  17.                 if (merge) {
  18.                     /* 如果是复制到已有的page上,那么就更新对应的frags的size */
  19.                     skb_shinfo(skb)->frags[i - 1].size +=
  20.                                     copy;
  21.                 } else {
  22.                     /* 这是一个新的page,那么需要填充新的frags的值 */
  23.                     skb_fill_page_desc(skb, i, page, off, copy);
  24.                     /* 增加page的计数,如果该page没有填满,且sock的sk_sndmsg_page没有值,则把当前                      page赋给它 */
  25.                     if (TCP_PAGE(sk)) {
  26.                         get_page(page);
  27.                     } else if (off + copy < PAGE_SIZE) {
  28.                         get_page(page);
  29.                         TCP_PAGE(sk) = page;
  30.                     }
  31.                 }
                 /* 调整偏移 */
  1.                 TCP_OFF(sk) = off + copy;
  2.             }
             
             /* 若没有复制任何数据,则取消PUSH标志 */
  1.             if (!copied)
  2.                 TCP_SKB_CB(skb)->flags &= ~TCPHDR_PSH;
             
             /* 
             调整sequence
             注意这里的sequence number并不是tcp包中的sequence number。这里的sequence是tcp内部使用              的
             */
  1.             tp->write_seq += copy;
  2.             TCP_SKB_CB(skb)->end_seq += copy;
  3.             skb_shinfo(skb)->gso_segs = 0;

  4.             from += copy;
  5.             copied += copy;
  6.             if ((seglen -= copy) == 0 && iovlen == 0)
  7.                 goto out;

  8.             if (skb->len < max || (flags & MSG_OOB))
  9.                 continue;

  10.             if (forced_push(tp)) {
  11.                 /* 强制push, 即强制发送数据*/
  12.                 tcp_mark_push(tp, skb);
  13.                 __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
  14.             } else if (skb == tcp_send_head(sk)) //需要发送该skb
  15.                 tcp_push_one(sk, mss_now)
  16.             continue;

  1. wait_for_sndbuf:
  2.             set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
  3. wait_for_memory:
  4.             if (copied)
  5.                 tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);

  6.             if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
  7.                 goto do_error;
             /* 发送MSS */
  1.             mss_now = tcp_send_mss(sk, &size_goal, flags);
  2.         }
  3.     }

  4. out:
  5.     if (copied)
  6.         tcp_push(sk, flags, mss_now, tp->nonagle);
  7.     TCP_CHECK_TIMER(sk);
  8.     release_sock(sk);
  9.     return copied;
     /* 下面的都是错误处理 */
  1. do_fault:
  2.     if (!skb->len) {
  3.         tcp_unlink_write_queue(skb, sk);
  4.         /* It is the one place in all of TCP, except connection
  5.          * reset, where we can be unlinking the send_head.
  6.          */
  7.         tcp_check_send_head(sk, skb);
  8.         sk_wmem_free_skb(sk, skb);
  9.     }

  10. do_error:
  11.     if (copied)
  12.         goto out;
  13. out_err:
  14.     err = sk_stream_error(sk, flags, err);
  15.     TCP_CHECK_TIMER(sk);
  16.     release_sock(sk);
  17.     return err;
  18. }
至此,这个函数基本上学习完毕。这里可以看到TCP发送数据时,buffer的组织形式,如果支持SG的话,自然是用SG。如果不支持,当多个数据发送时,只能使用skb buffer来保存数据。这不仅降低效率,也占用了不必要的空间。
上一篇:leetcode1219 黄金旷工 middle


下一篇:剑指Offer:[第4天 查找算法(简单)]--->0~n-1中缺失的数字