- 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;
-
}