Tcp_sendmsg主要负责把数据从用户空间拷贝到内核空间,然后根据MSS分段,放入到SKB中,调用tcp_push发送出去。大部分代码都是在找应该把数据复制到哪里去。
int tcp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,
size_t size)
{
struct iovec *iov;
struct tcp_sock *tp = tcp_sk(sk);
struct sk_buff *skb;
int iovlen, flags;
int mss_now, size_goal;
int err, copied;
long timeo;
lock_sock(sk);
TCP_CHECK_TIMER(sk);
flags = msg->msg_flags;
timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT); /* 如果send_msg是阻塞操作的话,获取阻塞的时间 */
/* Wait for a connection to finish. */
if ((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT))/* 发送用户数据应该处于ESTABLISHED状态或者是CLOSE_WAIT状态, 如果不在这两种状态则调用sk_stream_wait_connnect 等连接建立完成,如果超时的话就跳转到out_err*/
if ((err = sk_stream_wait_connect(sk, &timeo)) != 0)
goto out_err;
/* This should be in poll */
clear_bit(SOCK_ASYNC_NOSPACE, &sk->sk_socket->flags);
/* 获取当前的MSS, 并将MSG_OOB清零,因为OOB带外数据不支持GSO*/
mss_now = tcp_current_mss(sk, !(flags&MSG_OOB));
/* 获取SKB的最大长度,这个表示SKB到达网络设备上时的最大长度*/
size_goal = tp->xmit_size_goal;
/* Ok commence sending. */
iovlen = msg->msg_iovlen; /* 待发数据块的块数*/
iov = msg->msg_iov; /*待发数据指针,起始地址*/
copied = 0; /* copied表示有多少个数据块已经从用户空间复制到内核空间,先清零*/
err = -EPIPE;/* 先把错误谁-EPIPE,EPIPE表示本地已经关闭socket连接了*/
if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN)) /* 如果本地socket有错误或者不允许发送数据,那么跳到do_error去处理*/
goto do_error;
while (--iovlen >= 0) {/* 如果还有待拷贝的数据块,这个循环用于控制拷贝所有的用户数据块到内核空间*/
int seglen = iov->iov_len; /*当前要复制的这个数据块的长度*/
unsigned char __user *from = iov->iov_base; /*当前要复制的这个数据块的起始地址*/
iov++; /*指向下一个数据块*/
while (seglen > 0) {/*这个数据块是不是全部都