Linux内核协议栈(4) 跟踪sendto调用

int tcp_sendmsg(struct sock *sk, struct msghdr *msg, size_t size)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct sk_buff *skb;
	int flags, err, copied = 0;
	int mss_now = 0, size_goal, copied_syn = 0;
	bool sg;
	long timeo;

	lock_sock(sk);

	flags = msg->msg_flags;
	if (flags & MSG_FASTOPEN) {									//tcpfastopen特性
		err = tcp_sendmsg_fastopen(sk, msg, &copied_syn, size);
		if (err == -EINPROGRESS && copied_syn > 0)
			goto out;
		else if (err)
			goto out_err;
	}

	timeo = sock_sndtimeo(sk, flags & MSG_DONTWAIT);//如果不是非阻塞IO,则得到超时时间

	/* Wait for a connection to finish. One exception is TCP Fast Open
	 * (passive side) where data is allowed to be sent before a connection
	 * is fully established.
	 */
	if (((1 << sk->sk_state) & ~(TCPF_ESTABLISHED | TCPF_CLOSE_WAIT)) &&
	    !tcp_passive_fastopen(sk)) {
		err = sk_stream_wait_connect(sk, &timeo);//如果没有建立连接或连接关闭,则等待连接完成
		if (err != 0)
			goto do_error;
	}
	
	//发送到接受队列中
	if (unlikely(tp->repair)) {
		if (tp->repair_queue == TCP_RECV_QUEUE) {
			copied = tcp_send_rcvq(sk, msg, size);
			goto out_nopush;
		}

		err = -EINVAL;
		if (tp->repair_queue == TCP_NO_QUEUE)
			goto out_err;

		/* 'common' sending to sendq */
	}

	/* This should be in poll */
	sk_clear_bit(SOCKWQ_ASYNC_NOSPACE, sk);//清除异步情况下队列满标志
	
	/* 获取当前的发送MSS. 
     * 获取可发送到网卡的最大数据长度,如果使用GSO,会是MSS的整数倍。 
     */  
	mss_now = tcp_send_mss(sk, &size_goal, flags);

	/* Ok commence sending. */
	copied = 0;

	   /* 如果连接有错误,或者不允许发送数据了,那么返回-EPIPE */  
	err = -EPIPE;
	if (sk->sk_err || (sk->sk_shutdown & SEND_SHUTDOWN))
		goto out_err;

	sg = !!(sk->sk_route_caps & NETIF_F_SG);/* 网卡是否支持分散聚合 */  

	 /* 遍历用户层的数据块数组 */  
	while (msg_data_left(msg)) {
		int copy = 0;
		int max = size_goal;

		skb = tcp_write_queue_tail(sk); //重传队列其实就是发送队列(sk->sk_write_queue),保存着发送且未得到确认的数据
		
		if (tcp_send_head(sk)) {		//sk->sk_send_head指向当前要发送buff的位置,如果为空,说明buf没有空间了

			 /* 如果网卡不支持检验和计算,那么skb的最大长度为MSS,即不能使用GSO */  
			if (skb->ip_summed == CHECKSUM_NONE)
				max = mss_now;			//最大报文长度
			copy = max - skb->len;
		}

		/* 需要使用新的skb来装数据 */  
		if (copy <= 0) {
new_segment:
			/* Allocate new segment. If the interface is SG,
			 * allocate skb fitting to single page.
			 */


		   /* 如果发送队列的总大小sk_wmem_queued大于等于发送缓存的上限sk_sndbuf, 
            * 或者发送缓存中尚未发送的数据量超过了用户的设置值,就进入等待。 
            */ 
              
			if (!sk_stream_memory_free(sk))
				goto wait_for_sndbuf;


            /* 申请一个skb,其线性数据区的大小为: 
             * 通过select_size()得到的线性数据区中TCP负荷的大小 + 最大的协议头长度。 
             * 如果申请skb失败了,或者虽然申请skb成功,但是从系统层面判断此次申请不合法, 
             * 那么就进入睡眠,等待内存。 
             */ 
			skb = sk_stream_alloc_skb(sk,
						  select_size(sk, sg),
						  sk->sk_allocation,
						  skb_queue_empty(&sk->sk_write_queue));
			if (!skb)
				goto wait_for_memory;

			/*
			 * Check whether we can use HW checksum.
			 *
			 *如果网卡支持校验和的计算,那么由硬件计算报头和首部的校验和。 
			 */
			if (sk->sk_route_caps & NETIF_F_ALL_CSUM)
				skb->ip_summed = CHECKSUM_PARTIAL;

			skb_entail(sk, skb);
			copy = size_goal;
			max = size_goal;

			/* All packets are restored as if they have
			 * already been sent. skb_mstamp isn't set to
			 * avoid wrong rtt estimation.
			 *
			 *  如果使用了TCP REPAIR选项,那么为skb设置“发送时间”。 
			 */
			if (tp->repair)
				TCP_SKB_CB(skb)->sacked |= TCPCB_REPAIRED;
		}

		/* Try to append data to the end of skb. */
		if (copy > msg_data_left(msg))
			copy = msg_data_left(msg);

		/* Where to copy to?
		 *
		 * 如果skb的线性数据区还有剩余空间,就先复制到线性数据区。 
		 *
		 */
		if (skb_availroom(skb) > 0) {
			/* We have some space in skb head. Superb! */
			copy = min_t(int, copy, skb_availroom(skb));

			 /* 拷贝用户空间的数据到内核空间,同时计算校验和 */  
			err = skb_add_data_nocache(sk, skb, &msg->msg_iter, copy);
			if (err)
				goto do_fault;
			
			
		} else {/* 如果skb的线性数据区已经用完了,那么就使用分页区 */  
			bool merge = true;
			int i = skb_shinfo(skb)->nr_frags;
			struct page_frag *pfrag = sk_page_frag(sk);


			    /* 检查分页是否有可用空间,如果没有就申请新的page。 
                 * 如果申请失败,说明系统内存不足。 
                 * 之后会设置TCP内存压力标志,减小发送缓冲区的上限,睡眠等待内存。 
                 */  
			if (!sk_page_frag_refill(sk, pfrag))
				goto wait_for_memory;

			if (!skb_can_coalesce(skb, i, pfrag->page,
					      pfrag->offset)) {

					/* 不能追加时,检查分页数是否达到了上限,或者网卡不支持分散聚合。 
                     * 如果是的话,就为此skb设置PSH标志,尽快地发送出去。 
                     * 然后跳转到new_segment处申请新的skb,来继续填装数据。 
                     */  
				if (i == MAX_SKB_FRAGS || !sg) {
					tcp_mark_push(tp, skb);
					goto new_segment;
				}
				merge = false;
			}

			copy = min_t(int, copy, pfrag->size - pfrag->offset);

			if (!sk_wmem_schedule(sk, copy))
				goto wait_for_memory;

			    /* 拷贝用户空间的数据到内核空间,同时计算校验和。 
                 * 更新skb的长度字段,更新sock的发送队列大小和预分配缓存。 
                 */  
			err = skb_copy_to_page_nocache(sk, &msg->msg_iter, skb,
						       pfrag->page,
						       pfrag->offset,
						       copy);
			if (err)
				goto do_error;

			/* Update the skb. */
			
			if (merge) {/* 如果把数据追加到最后一个分页了,更新最后一个分页的数据大小 */ 
				skb_frag_size_add(&skb_shinfo(skb)->frags[i - 1], copy);
			} else {

				  /* 初始化新增加的页 */  
				skb_fill_page_desc(skb, i, pfrag->page,
						   pfrag->offset, copy);
				get_page(pfrag->page);
			}
			pfrag->offset += copy;
		}
		
		 /* 如果这是第一次拷贝,取消PSH标志 */  
		if (!copied)
			TCP_SKB_CB(skb)->tcp_flags &= ~TCPHDR_PSH;

		tp->write_seq += copy;	 /* 更新发送队列的最后一个序号 */  
		
		TCP_SKB_CB(skb)->end_seq += copy;/* 更新skb的结束序号 */  
		tcp_skb_pcount_set(skb, 0);

		copied += copy;	 /* 下次拷贝的地址 */ 
		if (!msg_data_left(msg)) {
			tcp_tx_timestamp(sk, skb);
			goto out;
		}

		    /* 如果skb还可以继续填充数据,或者发送的是带外数据,或者使用TCP REPAIR选项, 
             * 那么继续拷贝数据,先不发送。 
             */ 
		if (skb->len < max || (flags & MSG_OOB) || unlikely(tp->repair))
			continue;

		if (forced_push(tp)) {
			tcp_mark_push(tp, skb);
			__tcp_push_pending_frames(sk, mss_now, TCP_NAGLE_PUSH);
		} else if (skb == tcp_send_head(sk))
			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, size_goal);


				 /* 分两种情况: 
                 * 1. sock的发送缓存不足。等待sock有发送缓存可写事件,或者超时。 
                 * 2. TCP层内存不足,等待2~202ms之间的一个随机时间。 
                 */  
		err = sk_stream_wait_memory(sk, &timeo);
		if (err != 0)
			goto do_error;

		  /* 睡眠后MSS和TSO段长可能会发生变化,重新计算 */  
		mss_now = tcp_send_mss(sk, &size_goal, flags);
	}

out:
		  /* 如果已经有数据复制到发送队列了,就尝试立即发送 */  
	if (copied)
		tcp_push(sk, flags, mss_now, tp->nonagle, size_goal);
out_nopush:
	release_sock(sk);
	return copied + copied_syn;

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->sk_send_head */  
		
		sk_wmem_free_skb(sk, skb);/* 更新发送队列的大小和预分配缓存,释放skb */  
	}

do_error:
	if (copied + copied_syn)
		goto out;
out_err:
	err = sk_stream_error(sk, flags, err);
	/* make sure we wake any epoll edge trigger waiter */
	if (unlikely(skb_queue_len(&sk->sk_write_queue) == 0 && err == -EAGAIN))
		sk->sk_write_space(sk);
	release_sock(sk);
	return err;
}


static inline int select_size(struct sock *sk)
{
	struct tcp_sock *tp = tcp_sk(sk);
	int tmp = tp->mss_cache;/* Cached effective mss, not including SACKS  --> 用于缓存下刚刚计算所得的MSS */

	if (sk->sk_route_caps & NETIF_F_SG) {
		if (sk_can_gso(sk))	/* 什么东西? */
			tmp = 0;
		else {
			int pgbreak = SKB_MAX_HEAD(MAX_TCP_HEADER); //128+128

			if (tmp >= pgbreak &&
			    tmp <= pgbreak + (MAX_SKB_FRAGS - 1) * PAGE_SIZE)
				tmp = pgbreak;
		}
	}

	return tmp;
}



未完待续...

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值