Linux与BSD中TCP协议栈实现比较
作为两个最有名的开源操作系统,Linux和FreeBSD是网管们的首选。Linux以开放性和众多的驱动支持著称,而FreeBSD有着优良的UNIX传统,是公认的最稳定的操作系统。那么,在这两个操作系统间,该如何选择呢?幸好,我们有源码,可以从协议栈的实现中寻找答案。
TCP/IP协议栈是网络中广泛使用的事实网络通信标准。最初的TCP实现源自4.4BSDlite,在Linux兴起后,也不可避免得支持它。但Linux的实现自成体系,仅与传统实现保持接口上的兼容,下面我们将针对源码级的实现,来分析一下两者的异同。但是,对于Linux和FreeBSD这样优秀的系统来说,已经无所谓何优何劣,有的仅仅是实现策略与侧重点上的不同而已。
从进程的角度上讲,可以调用send,sendto,sendmsg来发送一段数据,来可以使用文件系统中的write和writev来发送数据。同理,接收数据可以使用相应的recv,recvmsg,recvfrom,也可以使用文件系统提供的read,readv来接收一段数据。对于接收来说,这是异步进行的,也就是说,这是中断驱动的,在以后的分析中,我们要注意这点。为简单起见,同时不失一般性,我们将分析TCP协议的输入输出全过程,并以已对LINUX及FreeBSD的实现作一对比。
首先我们来看FreeBSD上的协议实现,这也是最正统的实现。下面是完整的输入输出路径。
首先来看左边的输出,不管应用程序调用哪个输出函数,最终都要调用sosend来完成输出。Sosend将从用户空间把数据复制进内核管理的m_buf数据结构,m_buf是FreeBSD的TCP实现使用的数据缓冲结构。在sosend完成数据复制后,将调用TCP的输出函数,tcp_output要做的事情是分配一个新的m_buf来保存tcp头,并计算相应的数据校验码,在下一步的ip_output中,同样也要进行数据校验工作,并进行数据路由选择。最终ether_output将通过if_start来调用具体的硬件驱动程序来完成数据发送。在某个网卡的驱动中,ex_start将负责将数据从内核的m_buf缓冲复制进硬件自己的缓冲区,以完成数据发送工作。在这整个过程中,数据被复制两次,并且也被遍历两次(计算校验码),这也是主要的影响效率的地方。
再来讨论右边的输入,当网卡收到数据时,中断处理程序ex_intr将被调用。驱动通过ex_rx_intr将数据从硬件缓冲复制进m_buf数据结构中,并调用ether_input来进一步处理。ether_input通过ether_demux进行分用。如果是一个Ip包,将通过软中断调用ip_fastforward进行数据校验,并判断是否要转发,如果失败,将进行ip_input进行完整的处理。在in_input中,同样要判断是否要进行转发,如果不用,调用tcp_input进行进一步处理。在tcp_input中,进行数据校验和验证后,有一个叫做首部预测算法的优化,可以加快数据处理速度。进行完所有的操作后,如果是用户数据,将唤醒用户进程进行处理。同理,用户可以使用多个函数进行数据接收,而soreceive将负责将数据从m_buf转移至用户进程缓冲。
可以看出,在FreeBSD中,发送和接收数据,所进行的操作差不多,都要进行两次数据复制和两次数据遍历,这也是最大的影响效率的地方。两次数据复制似乎无可避免,下面我们来看看Linux是怎么做的。
可以看到,在LINUX上的实现稍显复杂。让我们首先从发送开始分析。在LINUX上,socket被实现为一个文件系统,这样可以通过vfs的write来调用,也可以直接使用send来调用,它们最终都是调用sock_sendmsg。Sock_sendmsg通过它的内核版本__sock_sendmsg直接调用tcp_sendmsg来发送数据。在tcp_sendmsg中,同时完成数据复杂和数据校验,这样节省了一次遍历操作,这也是和FreeBSD不同的地方。Linux使用skb结构来管理数据缓冲,这和FreeBSD的m_buf大同小异。当复制完数据后,使用tcp_push来进行下一步发送。Tcp_push通过__tcp_push_pending_frames来调用tcp_write_xmit将数据填入tcp的发送缓冲区。这里的填充仅是指针引用而已。下一步,tcp_transmit_skb将数据放入IP的发送队列。Ip_queue_xmit函数完成IP包头的设置以及数据效验,并调用ip_output进入下一步发送。如果不用分片,将使用ip_finish_output继续发送。在这里,填充数据的以太网包头后调用dev_queue_xmit函数来进一步处理。Dev_queue_xmit函数将数据转移至网络核心层的待发送队列,调用具体的驱动程序cp_start_xmit来完成数据的最终发送。最后的cp_start_xmit做的事情和freebsd的相应函数差不多,检查数据,并复制进硬件缓冲。
当接收到一个数据包的时候,网卡会产生中断,这样网卡驱动的cp_interrupt会被调用。cp_interrupt做的事情很少,只进行必要的检查后就返回了,更多的事情通过cp_rx_poll来完成,cp_rx_poll在软中断中被调用,这样做是为了提高驱动的处理效率。Cp_rx_poll做的事情主要就是把申请并将数据复制进一个skb缓冲中。netif_rx函数将数据从这个队列中转移至网络核心层队列中,netif_receive_skb从这里接收数据,调用ip_rcv来处理。Ip_rc和ip_rcv_finish一起检查数据,得到包的路由,并调用相应的input函数来完成路由,在这里就是ip_local_deliver,ip_local_deliver完成IP包的重组后,使用ip_local_deliver_finish来进入tcp的处理流程,tcp_v4_rcv完成数据校验以及一些简单的检查,主要的工作在tcp_v4_do_rcv中完成。tcp_v4_do_rcv先判断是否正常的用户数据,如果是则用tcp_rcv_established处理,否则用tcp_rcv_state_process来更新连接的状态机。tcp_rcv_established中同样有首部预测。如果一切顺便,将唤醒等待在tcp_recvmsg中的用户进程。后者将数据从skb缓冲中复制进用户进程缓冲。并进行逐级返回。
通过以上分析不难看出,Linux的代码比较混乱,可读性没有FreeBSD的好,比如说,Linux省略了以太网层,而且在接收数据中有多次异步操作,也许这将会影响内核的稳定性,FreeBSD的代码就比较清晰,程序处理一目了然,可读性也高,最稳定的操作系统名不虚传。这也可以从两个操作系统的起源得到解释。Linux起源于互联网时代,由众多爱好者一起完成,并没有一个完整的规划,代码也多次经过变动,而作者水平也参次不齐,造成现在的样子。而FreeBSD系出名门,一直由一个独立的小组进行维护,多年来更新不大,只有少许优化,所以代码的可读性非常高。但从另一方面讲,不断更新的Linux在代码方面比较激进,比如Linux使用skb缓冲效率要较FreeBSD使用的m_buf为高,这里限于篇幅,就不再具体分析了。而且linux发送数据时,在复制数据的同时完成的tcp的效验,这样就节省了一次数据的遍历操作。也提高了效率。
通过以上比较我们不难得出结论,如果效率为先,则Linux当为首推,如果稳定至上,FreeBSD应该为不二之选。不过,这两个操作系统都是非常优秀并久经考验,之间的差别也仅存于字面分析上。
ps:Linux 2.6 ip协议栈源码分析:http://download.csdn.net/detail/fenggui/9177853
Linux与BSD中TCP协议栈实现比较相关推荐
- linux 网络路径中网络协议栈有几种,linux网络路径中网络协议栈有几种
网络路径有很多种,其中的linux网络路径是最常用的,也是最需要关注的.linux网络路径中网络协议栈有几种?电脑新装系统漏洞应不应该修复?了解网络安全常识,首先就要了解计算机网络安全有哪些基本注意事 ...
- linux协议栈劫持,Linux系统优化之TCP协议栈优化-基本篇1
因为在做爬虫分布式系统的过程中,涉及到了一些linux系统优化方面的知识,所以来总结一下,我们会对linux的不同模块做相关的基本优化,这篇文章主要讲述的是关于tcp协议栈的参数优化. 1.机器环境 ...
- linux内核中TCP接收的实现
linux内核中TCP接收的实现入口函数是tcp_v4_rcv 1. 数据包检查处理 一开始做一些数据包详细检查处理,一旦出错,可能导致内核挂掉 int tcp_v4_rcv(struct sk_bu ...
- 解决Linux服务器中TCP的FIN_WAIT2,CLOSE_WAIT状态连接过多的问题
问题现象 Linux系统服务器中FIN_WAIT2,CLOSE_WAIT状态的TCP链接过多,服务不能及时响应. 通过命令 netstat –ant|grep FIN_WAIT2|wc –l 查看连接 ...
- 什么叫linux网络协议栈,我们为什么使用Linux内核的TCP协议栈
最近的一篇文章提出了"我们为什么使用Linux内核的TCP协议栈"的问题,并在Hacker News引发了非常有意思的讨论. 在CloudFlare的时候我也曾思考这个问题.我的经 ...
- 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系
00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...
- 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系
基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...
- linux数据包注释,关于 linux中TCP数据包(SKB)序列号的小笔记
关于 SKB序列号的小笔记 为了修改TCP协议,现在遇到了要改动tcp分组的序列号,但是只是在tcp_sendmsg函数中找到了SKB的end_seq 一直没有找到seq 不清楚在那里初始化了,就 ...
- Linux内核中TCP协议实现的关键数据结构
1. TCP协议头tcphdr TCP协议头描述了TCP数据段发送的源地址.目标地址.数据段传送管理和连接管理的信息,是TCP协议实现的重要数据结构之一. struct tcphdr {__be16 ...
- 用户态TCP协议栈的调研
一.各种用户态socket的对比 1.MTCP 简单介绍: 韩国高校的一个科研项目,在DPDK的2016年的技术开发者大会上有讲,所以intel将这个也放到了官方上,所以一般搜索DPDK的用户态的协议 ...
最新文章
- EOS技术及生态系统介绍
- 在 Spring Boot 中,如何干掉 if else
- hashmap 扩容是元素还是数组_HashMap的扩容机制---resize()
- 进程内COM与进程外COM
- 网络协议 9 - TCP协议(下):聪明反被聪明误
- Cygwin,Nutch安装配置,检验是否正确(对网友守望者博客的修改---在此感谢守望者)2
- 在C#调用C++的DLL简析(二)—— 生成托管dll
- java不可变类型_Java中的值类型:为什么它们不可变?
- HDOJ2035 人见人爱A^B
- 【PostgreSQL】PostgreSQL安装步骤
- linq 清除一条数据中的某个字段值_利用Postman中Tests断言校验返回结果
- 开源开放 | OpenKG发布cnSchema重构版本
- 【python】Python学到什么程度可以面试工作(解答一)
- 【转】Laravel - 从百草园到三味书屋 From Apprentice To Artisan目录
- android 图片3d,Android实现3D图片浏览效果示例
- 实战7:机器学习实战之 随机森林、逻辑回归、SVM算法方法进行垃圾邮件过滤分类 代码+数据
- EasyExcel 读取excel表 解决Empty row EasyExcel末尾出现非常多空白行跳过 EasyExcel跳过末尾空白行
- shell命令的使用
- pdf解密,pdf,jpg,word格式互相转换
- 计算机电源输出电压 电流,开关电源的输出电流如何决定_跟什么有关?