开发者社区> nothingfinal> 正文

TCP/IP源码(59)——TCP中的三个接收队列

简介: 作者:gfree.wind@gmail.com 博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net  微博:weibo.com/glinuxer QQ技术群:4367710   本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。
+关注继续查看
作者:gfree.wind@gmail.com
博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net
 
微博:weibo.com/glinuxer
QQ技术群:4367710
 
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。

======================================================================================================

在Linux内核的TCP实现中,TCP有三个接收队列——除去错误队列。这三个队列分别是struck sock中的sk_receive_queue和sk_backlog,以及struct tcp_sock中的prequeue。这三个队列作用,网上已经有很多文章论述了。这里只简单介绍一下,sk_receive_queue是真正的接收队列,收到的TCP数据包经过检查和处理后,就会保存到这个队列中。sk_backlog是当socket处于用户进程的上下文时(即用户正在对socket进行系统调用,如recv),Linux收到数据包时,在软中断处理过程中,会将数据包保存到sk_backlog中,然后直接返回。而prequeue则是在,该socket没有正在被用户进程使用时,由软中断直接将数据包保存在prequeue中,然后返回。


我们可以从tcp的接收处理函数中,验证上面的结果。下面的代码来自tcp_v4_rcv函数


	bh_lock_sock_nested(sk);
	ret = 0;
	if (!sock_owned_by_user(sk)) {
                /* 当sock没有被用户进程占有的时候,可以将数据包保存到prequeue中。失败的话,才进入tcp的真正的数据包处理过程 */
	        if (!tcp_prequeue(sk, skb))
		    ret = tcp_v4_do_rcv(sk, skb);
	} else if (unlikely(sk_add_backlog(sk, skb))) {
                /* 当sock被用户进程占有时,将数据包保存到backlog中,然后返回 */
		bh_unlock_sock(sk);
		NET_INC_STATS_BH(net, LINUX_MIB_TCPBACKLOGDROP);
		goto discard_and_relse;
	}
	bh_unlock_sock(sk);
在sock被用户进程占有时,kernel将数据包保存到backlog中,这个是可以理解的。因为即使是软中断处理流程,也需要尽快完成,好让kernel尽快处理下一个数据包。那么当sock不被用户进程占有时,kernel将数据包保存到prequeue中,自然也是这个道理。这些数据包会在用户进程调用receive的时候,再进行TCP数据包完整的处理流程。


网上的大部分资料,都只是介绍了这三个队列的用途。但是看到这里,其实我们应该有一个疑问。backlog和prequeue都是保存的未经处理的数据,为什么需要两个不同的队列呢?为了解答这个疑问,我们需要研究一下prequeue和backlog是如何应用的?前面是两个队列的写入操作,下面看看两个队列何时被读取。prequeue的处理函数tcp_prequeue_process,如前文所说,在TCP的读取数据函数tcp_recvmsg中调用。在tcp_recvmsg的入口,会调用lock_sock来设置sk->sk_lock.owned,表示该sock由用户进程占有,然后会对receive_queue和prequeue中的数据包进行处理。正因为sock被用户进程处理时,会访问prequeu,所以软中断只能将数据保存到backlog中,以避免竞争。那么为什么在sock不由用户进程占有时,只能保存到prequeu中,而不能重入backlog呢?


让我们继续跟进,看看何时处理backlog的数据包。Oh,my god,居然是在__release_sock中,这真的有点出乎我的意料。这也就解释了,为什么需要两个队列来保存未处理数据包。对于sock来说,一共有两种状态:1. 用户进程占用该sock;2. 用户进程未占用该sock;而kernel需要在任何情况下,都要能够保证tcp数据包处理的软中断快速返回。而保存未处理数据包的队列,无论如何也要在上述的一个情况下,访问未处理的数据包。那么这不可避免的会有资源竞争。所以为了避免这种情况,当sock被用户进程占用时,让它处理prequeue中的数据包,软中断则往backlog中保存。当sock不被用户进程占用时,会去访问backlog中的数据包,软中断则往prequeue中保存。




版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
14744 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
25240 0
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
20698 0
TCP/IP源码(59)——TCP中的三个接收队列
作者:gfree.wind@gmail.com 博客:blog.focus-linux.net   linuxfocus.blog.chinaunix.net  微博:weibo.com/glinuxer QQ技术群:4367710   本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。
644 0
TCP/IP源码(20)——数据包发送的流程图
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net 今天,把之前看过的UDP发送的过程总结了一下,画出了数据包发送的流程图。
1050 0
TCP/IP源码(22)——tcp_sendmsg(1)
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.
1074 0
TCP/IP源码(21)——struct inet_socks
本文的copyleft归gfree.wind@gmail.com所有,使用GPL发布,可以自由拷贝,转载。但转载请保持文档的完整性,注明原作者及原链接,严禁用于任何商业用途。 作者:gfree.wind@gmail.com 博客:linuxfocus.blog.chinaunix.net     在前面的博文中,我画出了TCP/IP数据包发送的流程图。
1050 0
+关注
nothingfinal
软件开发,安全加密
1069
文章
341
问答
文章排行榜
最热
最新
相关电子书
更多
JS零基础入门教程(上册)
立即下载
性能优化方法论
立即下载
手把手学习日志服务SLS,云启实验室实战指南
立即下载