本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以*拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
作者:gfree.wind@gmail.com
博客:linuxfocus.blog.chinaunix.net
- int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
- size_t size)
-
{
- /*
- 省略之前的代码
- */
- while (--iovlen >= 0) {
- size_t seglen = iov->iov_len;
- unsigned char __user *from = iov->iov_base;
- iov++;
- while (seglen > 0) {
-
- /*
- 省略了之前的代码
- */
- /*
- 调整要复制的字节数,使最多只能复制剩下的字节数seglen。
- 从后面可以看出,seglen逐渐减少
- */
- /* Try to append data to the end of skb. */
- if (copy > seglen)
- copy = seglen;
- /* Where to copy to? */
- if (skb_tailroom(skb) > 0) {
- /* 如果skb还有空间,则复制到skb中去*/
- /* We have some space in skb head. */
- /*
- 调整copy的大小,使其不得大于tailroom的空间。
- */
- if (copy > skb_tailroom(skb))
- copy = skb_tailroom(skb);
- /* 将数据加到skb中去*/
- if ((err = skb_add_data(skb, from, copy)) != 0)
- goto do_fault;
- } else {
- /* skb中没有空闲空间了 */
- /*
- 这时的数据组织方式,可以参考我的另外一篇博文《tcp/ip源码(19)——Scatter/Gather I /O在L3中的应用》
- */
- int merge = 0;
- int i = skb_shinfo(skb)->nr_frags;
- struct page *page = TCP_PAGE(sk);
- int off = TCP_OFF(sk);
- if (skb_can_coalesce(skb, i, page, off) &&
- off != PAGE_SIZE) {
- /* We can extend the last page
- * fragment. */
- /* 可以将数据加到最后一个page中 */
- merge = 1;
- } else if (i == MAX_SKB_FRAGS || !sg) {
- /* Need to add new fragment and cannot
- * do this because interface is non-SG,
- * or because all the page slots are
- * busy. */
- /*
- 到达此处,表明当前的page无法填充数据。
- 这时,数据分片达到最大的frag数量,或者不支持Scatter Gather功能。那么都无法继续 填充。这时,将tcp置为push标志,尽快发送数据
- */
- tcp_mark_push(tp, skb);
- /* 需要申请一个新的skb */
- goto new_segment;
- } else if (page) {
- if (off == PAGE_SIZE) {
- /*
- 之前的page已经写满.所以该page已经不能再继续填充了,
- 因此将sk->sk_sndmsg_page和page置为null
- */
- put_page(page);
- TCP_PAGE(sk) = page = NULL;
- off = 0;
- }
- } else
- off = 0; //没有可用page,所以offset为0
/* 调整copy的大小,使之不得大于page剩下的空间 */
- if (copy > PAGE_SIZE - off)
- copy = PAGE_SIZE - off;
/* 判断是否需要等待,直到有许可的内存使用 */
- if (!sk_wmem_schedule(sk, copy))
- goto wait_for_memory;
- if (!page) {
- /* 如当前没有page,则申请一个新的page */
- /* Allocate new cache page. */
- if (!(page = sk_stream_alloc_page(sk)))
- goto wait_for_memory;
- }
/* 复制数据到当前page */
- /* Time to copy data. We are close to
- * the */
- err = skb_copy_to_page(sk, from, skb, page,
- off, copy);
- if (err) {
- /* If this page was new, give it to the
- * socket so it does not get leaked.
- */
- /* 出错了。则把这个page交给该socket,所以没有内存泄露 */
- if (!TCP_PAGE(sk)) {
- TCP_PAGE(sk) = page;
- TCP_OFF(sk) = 0;
- }
- goto do_error;
- }
- /* Update the skb. */
- if (merge) {
- /* 如果是复制到已有的page上,那么就更新对应的frags的size */
- skb_shinfo(skb)->frags[i - 1].size +=
- copy;
- } else {
- /* 这是一个新的page,那么需要填充新的frags的值 */
- skb_fill_page_desc(skb, i, page, off, copy);
- /* 增加page的计数,如果该page没有填满,且sock的sk_sndmsg_page没有值,则把当前 page赋给它 */
- if (TCP_PAGE(sk)) {
- get_page(page);
- } else if (off + copy < PAGE_SIZE) {
- get_page(page);
- TCP_PAGE(sk) = page;
- }
- }
/* 调整偏移 */
- TCP_OFF(sk) = off + copy;
- }
/* 若没有复制任何数据,则取消PUSH标志 */
- if (!copied)
- TCP_SKB_CB(skb)->flags &= ~TCPHDR_PSH;
/*
调整sequence
注意这里的sequence number并不是tcp包中的sequence number。这里的sequence是tcp内部使用 的
*/
- tp->write_seq += copy;
- TCP_SKB_CB(skb)->end_seq += copy;
- skb_shinfo(skb)->gso_segs = 0;
- from += copy;
- copied += copy;
- if ((seglen -= copy) == 0 && iovlen == 0)
- goto out;
- if (skb->len < max || (flags & MSG_OOB))
- continue;
- if (forced_push(tp)) {
- /* 强制push, 即强制发送数据*/
- tcp_mark_push(tp, skb);
- __tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
- } else if (skb == tcp_send_head(sk)) //需要发送该skb
- tcp_push_one(sk, mss_now);
- continue;
- wait_for_sndbuf:
- set_bit(SOCK_NOSPACE, &sk->sk_socket->flags);
- wait_for_memory:
- if (copied)
- tcp_push(sk, flags & ~MSG_MORE, mss_now, TCP_NAGLE_PUSH);
- if ((err = sk_stream_wait_memory(sk, &timeo)) != 0)
- goto do_error;
/* 发送MSS */
- mss_now = tcp_send_mss(sk, &size_goal, flags);
- }
- }
- out:
- if (copied)
- tcp_push(sk, flags, mss_now, tp->nonagle);
- TCP_CHECK_TIMER(sk);
- release_sock(sk);
- return copied;
/* 下面的都是错误处理 */
- do_fault:
- if (!skb->len) {
- tcp_unlink_write_queue(skb, sk);
- /* It is the one place in all of TCP, except connection
- * reset, where we can be unlinking the send_head.
- */
- tcp_check_send_head(sk, skb);
- sk_wmem_free_skb(sk, skb);
- }
- do_error:
- if (copied)
- goto out;
- out_err:
- err = sk_stream_error(sk, flags, err);
- TCP_CHECK_TIMER(sk);
- release_sock(sk);
- return err;
- }
至此,这个函数基本上学习完毕。这里可以看到TCP发送数据时,buffer的组织形式,如果支持SG的话,自然是用SG。如果不支持,当多个数据发送时,只能使用skb buffer来保存数据。这不仅降低效率,也占用了不必要的空间。