理解TCP/IP协议栈(2)

翻译自: https://www.cubrid.org/blog/understanding-tcp-ip-network-stack

数据接收

现在来看看数据是如何接收的. 数据接收就是网络协议栈处理到达数据的过程. 如Figure 3所示:

Figure 3: TCP/IP栈各层如何处理到达数据的过程.

  • 首先NIC将包写入它的内存. 它会通过CRC来检查包是否有效, 然后将数据发送到主机的内存缓冲区. 这个缓冲区已经由Driver向内核请求获取并且用来填充接收到的数据包. 当缓冲区开辟后, 驱动通知NIC对应的内存地址和大小. 当驱动没有可用的内存缓冲区而NIC不断接收数据时, NIC会丢掉接收到的数据包.
    在发送数据到主机内存缓冲区后, NIC向主机系统发送一个中断信号.
  • 然后, Driver检查是否能处理新到达的数据包. 到这一步, 会用到driver-NIC通讯协议.
  • 当driver决定向上一层发送数据包时, 需要封装成当前系统能识别的包结构. 比如, Linux中的sk_buff, BSD系列内核的mbuf, 以及windows的NET_BUFFER_LIST等包结构.
  • 以太网链路层检查包是否有效并多路复用上层协议(网络协议). 这时它使用到Ethernet头的以太网类型值(IPv4以太网类型值为0x0800). 以太网头被去掉并把其余数据发送到IP层.
  • IP层同样会检查包的有效性, 这里检查的是IP头校验码. 它会决定是本地系统处理该数据包, 还是转发到其他系统. 如果数据需要在本地处理, IP层将根据IP头来多路复用上层协议(传输协议). TCP原型值为6. IP头信息被移除后, 数据将被发送到传输层.
  • 同样, 传输层也检查数据的有效性, 检查的是TCP校验码. 之前提到, 目前网络栈一般使用checksum offload, TCP checksum由网卡而非内核来计算.然后会搜索数据包所要连接的TCP控制块. 这时包中的

网络栈发展历程

目前所提到的网络栈结构都是最基础的功能. 1990年代的网络栈的功能并没有比上面提到的功能多多少. 但是最近的网络栈拥有了更过功能以及实现得更加复杂.
目前网络栈可以按目的进行如下分类:

数据包处理过程

这里有类似Netfilter的功能 (防火墙, NAT) 以及传输控制. 通过在基础处理流中加入用户自定义代码,这部分的功能可以依据用户配置来执行不同的处理操作.

协议性能

目的是在给定的网络环境中, TCP协议能尽可能的提高吞吐能力和稳定性,减少延迟. 一些拥塞控制算法和TCP方法, 比如SACK是经典的应用例子. 协议的改进并不在本文的讨论范围,因此暂时忽略.

包处理效率

包处理效率目标是通过减少处理包所消耗的CPU周期,内存使用及访问等参数,来提高每秒能处理包的数量. 在减少系统延迟上已经有一些解决方案, 包括栈并行处理,header预测,zero-copy,single-copy,checksum offload, TSO, LRO, RSS等等.

控制流

现在再来详细看看Linux网络栈中的内部流. 类似一个非网络栈的子系统,网络栈以事件驱动的方式来运行. 因此并没有独立的线程去执行栈过程. Figure 1及Figure 3 展示了控制流的简化图表, 而Figure 4 展示了更准确的控制流过程.


Figure 4: Control Flow in the Stack.

  • (1)应用程序请求系统调用来使用TCP, 比如调用read和write方法, 但此时并没有进行包传输.
  • (2) 和(1)一样, 开始请求包传输. 它创建一个数据包并且会将包向下传送到网卡驱动. 在驱动之前有一个传输队列, 包首先会传输到这个队列, 然后队列再决定什么时候将这个包传输到驱动. 这是Linux的队列规则(qdisc). Linux的流量控制功能就是将qdisc扶助实践, 而默认的qdisc就是FIFO队列. 通过另一种qdisc, 可以实现不同效果, 比如伪造丢包, 包延迟, 传输速率限制等. (1) 和(2)中, 应用进程都有调用驱动程序.
  • (3)中展示了TCP所用的时钟超时示例. 比如, 当TIME_WAIT 的时钟超时后, 会调用TCP去关闭这个连接.
  • 和(3)类似, (4)中TCP时钟超时,这时TCP的执行结果应该被通知回去. 比如, 当重传时钟超时后, 没有收到ACK响应的数据包会被重新传输.(3) 和(4)阐述了处理时钟中断的软中断流程.
  • 当网卡驱动收到一个中断信号, 它会释放已经传输的包. 通常驱动处理在这里便结束. (5)中, 驱动请求软中断,软中断处理器便处理传输队列并将等待的包传输到驱动.
  • 当网卡驱动接收到中断并发现一个新到达的包,它会请求softirq. 处理接收包的softirq调用驱动并将该包传输到上层应用. Linux中, 像上面展示的接收包处理过程称为New API (NAPI). 和polling类似,因为驱动并没有直接将包传输到上层, 真正执行的代码叫NAPI poll.
    (6)中展示了TCP的操作完成而(7)则是TCP请求更多的包传输. 流程(5), (6)及 (7)均由NIC中断softirq执行处理.

你可能感兴趣的:(Networking)