作为两个最有名的开源操作系统,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协议栈实现比较相关推荐

  1. linux 网络路径中网络协议栈有几种,linux网络路径中网络协议栈有几种

    网络路径有很多种,其中的linux网络路径是最常用的,也是最需要关注的.linux网络路径中网络协议栈有几种?电脑新装系统漏洞应不应该修复?了解网络安全常识,首先就要了解计算机网络安全有哪些基本注意事 ...

  2. linux协议栈劫持,Linux系统优化之TCP协议栈优化-基本篇1

    因为在做爬虫分布式系统的过程中,涉及到了一些linux系统优化方面的知识,所以来总结一下,我们会对linux的不同模块做相关的基本优化,这篇文章主要讲述的是关于tcp协议栈的参数优化. 1.机器环境 ...

  3. linux内核中TCP接收的实现

    linux内核中TCP接收的实现入口函数是tcp_v4_rcv 1. 数据包检查处理 一开始做一些数据包详细检查处理,一旦出错,可能导致内核挂掉 int tcp_v4_rcv(struct sk_bu ...

  4. 解决Linux服务器中TCP的FIN_WAIT2,CLOSE_WAIT状态连接过多的问题

    问题现象 Linux系统服务器中FIN_WAIT2,CLOSE_WAIT状态的TCP链接过多,服务不能及时响应. 通过命令 netstat –ant|grep FIN_WAIT2|wc –l 查看连接 ...

  5. 什么叫linux网络协议栈,我们为什么使用Linux内核的TCP协议栈

    最近的一篇文章提出了"我们为什么使用Linux内核的TCP协议栈"的问题,并在Hacker News引发了非常有意思的讨论. 在CloudFlare的时候我也曾思考这个问题.我的经 ...

  6. 【Linux网络编程】TCP网络编程中connect listen和accept三者之间的关系

    00. 目录 文章目录 00. 目录 01. TCP服务端和客户端流程 02. connect函数 03. listen函数 04. 三次握手 05. accept函数 06. 附录 01. TCP服 ...

  7. 【Linux网络编程】TCP网络编程中connect()、listen()和accept()三者之间的关系

    基于 TCP 的网络编程开发分为服务器端和客户端两部分,常见的核心步骤和流程如下: connect()函数 对于客户端的 connect() 函数,该函数的功能为客户端主动连接服务器,建立连接是通过三 ...

  8. linux数据包注释,关于 linux中TCP数据包(SKB)序列号的小笔记

    关于  SKB序列号的小笔记 为了修改TCP协议,现在遇到了要改动tcp分组的序列号,但是只是在tcp_sendmsg函数中找到了SKB的end_seq  一直没有找到seq 不清楚在那里初始化了,就 ...

  9. Linux内核中TCP协议实现的关键数据结构

    1. TCP协议头tcphdr TCP协议头描述了TCP数据段发送的源地址.目标地址.数据段传送管理和连接管理的信息,是TCP协议实现的重要数据结构之一. struct tcphdr {__be16 ...

  10. 用户态TCP协议栈的调研

    一.各种用户态socket的对比 1.MTCP 简单介绍: 韩国高校的一个科研项目,在DPDK的2016年的技术开发者大会上有讲,所以intel将这个也放到了官方上,所以一般搜索DPDK的用户态的协议 ...

最新文章

  1. EOS技术及生态系统介绍
  2. 在 Spring Boot 中,如何干掉 if else
  3. hashmap 扩容是元素还是数组_HashMap的扩容机制---resize()
  4. 进程内COM与进程外COM
  5. 网络协议 9 - TCP协议(下):聪明反被聪明误
  6. Cygwin,Nutch安装配置,检验是否正确(对网友守望者博客的修改---在此感谢守望者)2
  7. 在C#调用C++的DLL简析(二)—— 生成托管dll
  8. java不可变类型_Java中的值类型:为什么它们不可变?
  9. HDOJ2035 人见人爱A^B
  10. 【PostgreSQL】PostgreSQL安装步骤
  11. linq 清除一条数据中的某个字段值_利用Postman中Tests断言校验返回结果
  12. 开源开放 | OpenKG发布cnSchema重构版本
  13. 【python】Python学到什么程度可以面试工作(解答一)
  14. 【转】Laravel - 从百草园到三味书屋 From Apprentice To Artisan目录
  15. android 图片3d,Android实现3D图片浏览效果示例
  16. 实战7:机器学习实战之 随机森林、逻辑回归、SVM算法方法进行垃圾邮件过滤分类 代码+数据
  17. EasyExcel 读取excel表 解决Empty row EasyExcel末尾出现非常多空白行跳过 EasyExcel跳过末尾空白行
  18. shell命令的使用
  19. pdf解密,pdf,jpg,word格式互相转换
  20. 计算机电源输出电压 电流,开关电源的输出电流如何决定_跟什么有关?

热门文章

  1. C语言:字符串转为long long --- atoll函数
  2. Android逆向不可不知的smali语言
  3. 百度云网盘批量复制文件,在线复制到每个文件夹中PC版
  4. 电脑编辑安卓分区_20分钟轻松解决安卓手机分区问题 干货篇
  5. 单片机PWM舵机控制原理
  6. 无版权高清图片素材库pixabay
  7. 程序设计基础知识点整理,超全!!!
  8. 达梦数据库的简单使用
  9. 32位qt程序, 利用32位mysql驱动,连接64位mysql8.0
  10. cpu性能天梯图服务器 4210,手机CPU性能天梯图