prequeue和backlog和receive

网上找了很多文章,对于基础不太好的我来说,还是不懂到底什么时候skb挂到prequeue中,又是什么时候挂到backlog中,或者receive中。

于是只能不停的看代码,尽量每一行都能看懂。功夫不负有心人,终于理解了。在此做一下笔记。


首先,有2种方式表示标识用户正在调用recv函数,及内核的tcp_recvmsg:

1:sk->sk_lock.owned,其值为1表示有进程进入了tcp_recvmsg函数,并且执行了lock_sock。

注意:sk->sk_lock.owned其值为0,肯定是被release了,但是tcp_recvmsg函数中,并不是仅仅执行完函数,退出时,才会执行release。

在sk_wait_data函数中,让进程休眠时,也会release。


2:tp->ucopy.task

不等于null,表示进程正在读数据。


1和2的各个组合,正好能够表示进程的状态:


组合1:

sk->sk_lock.owned ==0 && tp->ucopy.task == null

要么就是压根没调用tcp_recvmsg,要么是调用后,退出了tcp_recvmsg


组合2:

sk->sk_lock.owned ==1 && tp->ucopy.task == null

执行中,即进程上下文中,进程已经进入tcp_recvmsg,但是还没读,准备读。


组合3:

sk->sk_lock.owned ==1 && && tp->ucopy.task ==null

进程进入tcp_recvmsg,并且正在读。


组合4:

sk->sk_lock.owned ==0 && && tp->ucopy.task != null


组合4最最重要!!!!!

tcp_do_rcv函数中,当sk->sk_lock.owned ==0 时,就会去执行tcp_prequeue。

tcp_prequeue中,就是判断如果tp->ucopy.task != null,就把skb挂到prequeue中。

而组合4出现的愿意,就是recv系统调用调用tcp_reccmsg之后,发现skb中的字节数,不够我们需要拷贝的字节数,导致进程休眠,即阻塞形系统调用导致进程休眠,

这种情况下,会将skb挂到prequeue中。



如果sk->sk_lock.owned ==0并且tp->ucopy.task == null,那就乖乖的挂到receive中。

如果sk->sk_lock.owned ==1 ,表示进程正在处于recv调用中,可能在执行代码的某个部分,反正在执行,进程没有休眠,也没有退出。。等等,为了软中断和进程上下文同步,将收到的skb挂到backlog中。进程在release_sock()中会处理backlog队列中的数据。



/* Packet is added to VJ-style prequeue for processing in process
 * context, if a reader task is waiting. Apparently, this exciting
 * idea (VJ's mail "Re: query about TCP header on tcp-ip" of 07 Sep 93)
 * failed somewhere. Latency? Burstiness? Well, at least now we will
 * see, why it failed. 8)8)				  --ANK
 *
 * NOTE: is this not too big to inline?
 */
//能进入这个函数,表示sk->sk_lock.owned ==0
static inline int tcp_prequeue(struct sock *sk, struct sk_buff *skb)
{
	struct tcp_sock *tp = tcp_sk(sk);

	if (sysctl_tcp_low_latency || !tp->ucopy.task)
		return 0;
//sysctl_tcp_low_latency 默认为0,!tp->ucopy.task不等于null,表示正在读取数据,而进入函数的条件又是进程没有锁住,
//出现这种现象的唯一情况,就是进程没有读够想要的数据,休眠了

	__skb_queue_tail(&tp->ucopy.prequeue, skb);
	tp->ucopy.memory += skb->truesize;
	if (tp->ucopy.memory > sk->sk_rcvbuf) {
		struct sk_buff *skb1;

		BUG_ON(sock_owned_by_user(sk));

		while ((skb1 = __skb_dequeue(&tp->ucopy.prequeue)) != NULL) {
			sk_backlog_rcv(sk, skb1);
			NET_INC_STATS_BH(sock_net(sk),
					 LINUX_MIB_TCPPREQUEUEDROPPED);
		}

		tp->ucopy.memory = 0;
	} else if (skb_queue_len(&tp->ucopy.prequeue) == 1) {
//唤醒进程,进程立刻读取prequeue中的数据。
		wake_up_interruptible_poll(sk->sk_sleep,
					   POLLIN | POLLRDNORM | POLLRDBAND);
		if (!inet_csk_ack_scheduled(sk))
			inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK,
						  (3 * tcp_rto_min(sk)) / 4,
						  TCP_RTO_MAX);
	}
	return 1;
}




  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值