prequeue和backlog和receive

来源:互联网 发布:java前台工程师招聘 编辑:程序博客网 时间:2024/05/18 12:00

网上找了很多文章,对于基础不太好的我来说,还是不懂到底什么时候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 ==0static 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 0