点击蓝字
![647a76b462da633fef7c882f9c4992ab.gif](https://i-blog.csdnimg.cn/blog_migrate/01b40d476c2610f015801f920ecb5709.gif)
关注我们
![b7b429a8e84307dab2fbfa01a51a4739.png](https://i-blog.csdnimg.cn/blog_migrate/62973f96d23fce89e639445859ca9a3f.png)
作者简介
冯志明,2019年至今负责搜索算法的相关工作,擅长处理复杂的业务系统,对底层技术有浓厚兴趣。
简述
上一篇我们讨论了 Linux 网络 I/O 的结构。
为什么有这么多的结构,或者说为什么网络 I/O 这么复杂呢?
包括一些名词,比如 MSS,IFG 是什么也没讲清楚,本篇就来把这些讲清楚。
Linux 的网络 I/O 完全是基于“网络七层协议”而设计的,内核空间包含了从物理层到传输层。所以要想搞清 Linux 网络 I/O 的结构,需要讲清楚“网络七层协议”。
本篇文章作为学习 Linux 网络 I/O 的结构及网络 I/O 中的零拷贝相关原理的储备知识,讨论的重点在于“网络七层协议”对网络 I/O 的限制以及每一层对上一层提供的抽象,而不在于讨论协议本身。
OSI 体系结构有7个层次,每个层次都完成信息交换任务中一个相对独立的部分,具有特定的功能。
图-1
1 物理层
数据在物理层的表现就是不同“介质”中的“信号”。
常见的介质包括:电线、光纤、空气、真空等。对应的信号有:电信号,光信号,电磁信号。物理层做的就是把不同介质上的信号,抽象为0和1,作为信息在链路层的基础。
2 链路层
2.1 简介
信号是很容易受到干扰的,发送信号的设备也未必就能100%准确地发送信号。那么怎么才能保障信息的完整传递呢?
答案是:通过“帧间距”把0/1信号,抽象为数据帧,通过帧校验,确保一个帧的数据是完整的。
用“以太网”举例:
图-2
前导码(7 Byte),用于时钟同步;
帧开始符(1 Byte),标识帧的开始;
数据帧(Data frame),就是数据链路层的协议数据单元;
帧间距(IFG,Interframe Gap),12 Byte,相邻两帧之间的时间断,用于时钟数据恢复。
数据帧包括三部分:帧头,数据部分,帧尾。
帧头(18 Byte)包括: MAC目标地址(6 Byte), MAC源地址(6 Byte), 802.1Q标签(4 Byte) 以太类型(2 Byte)
数据部分(46–1500 Byte)
帧尾,用于校验数据部分(4 Byte)
2.2 抽象
经过链路层的抽象,数据在链路层的表现就是数据帧(frame) ;链路层提供给网络层操作的,就是数据帧中的数据部分,称之为报文/数据报(datagram)。
2.3 MTU/PMTU
MTU:(Maximum Transmit Unit),最大传输单元,是指链接层上能承载的报文的大小。
网络层必须遵守MTU限制,超过MTU限制的报文是无法发送的,会被抛弃。
以下是常见媒体的MTU表,以太网缺省 MTU=1500 Byte。
图-3
PMTU:(Path mtu),两台主机通信路径中的最小 MTU。两台主机之间的 PMTU 不一定是个常数,它取决于当时所选择的路径,而且路由选择也不一定是对称的(从 A 到 B 的路由可能与从 B 到 A 的路由不同),因此,PMTU 在两个方向上不一定是一致的。
图-4
2.4 测试你的 PMTU
使用 ping 命令时发现:当 ICMP 包的大小,大于1472时,传输就会失败,并提示 MTU=1500 ICMP 的头信息是8 byte,IP的头信息是20 byte,因此 MTU=1472+8+20=1500。
3 网络层(用IPv4协议举例)
3.1 简介
网际协议(英语:Internet Protocol,缩写:IP;也称互联网协议),是用于分组交换数据网络的一种协议,任务仅仅是根据源主机和目的主机的地址来传送数据。互联网协议提供的是一种“不可靠的”数据包传输机制(也被称作“尽力而为”或“尽最大努力交付”),IP 报文的发送,每次路径可能是不同的。
3.2 IPv4头信息
IPv4数据报,头部大小可变,一个典型的 IPv4头部包含20字节。
图-5
3.3 分片
IP 协议可以支持不同的链路层,就要适应不同的 MTU,因此 IP 报文被设计成可分片的。
当设备收到 IP 报文时,分析其目的地址并决定要在哪个链路上发送它。MTU 决定了数据载荷的最大长度,如 IP 报文长度比 MTU 大,则 IP 数据包必须进行分片。每一片的长度都小于等于 MTU 减去 IP 头的长度。
分片可能是多次的。
比如一个4,300字节的数据,在 MTU 为2,500字节的链路上传输,将进行如下分片。
图-6
假设下一跳的 MTU 为1,500字节,那么每一个分片都会被再次分成两片。
当一个接收者发现 IP 报文的 DF 标志为0,且分片偏移量不为0时,它便知道这个报文已被分片。接收者需要将分片的数据储存起来,当它收齐了一个报文的所有的分片,便可以将其按照正确的顺序组装起来,并交给上层协议栈。
3.5 IP分片带来的问题
分片和重组会消耗双方的 CPU 资源,并且对接收方来说,会占用大量的内存资源。
如果某个分片报文在网络传输过程中丢失,发送方必须重传所有分片报文,给网络资源带来额外的消耗。
黑客可以构造的分片报文,总是不发送最后一个分片报文,会导致接收方内存耗尽。
除第一个分片外,其他分片不包含四层协议的信息,因此防火墙不容易做控制策略。
3.6 抽象
数据在 IP 层被抽象为报文/数据报(Datagram),而数据分组称为分片(Fragment)。IP层提供给传输层的,就是把"逻辑上完整的数据包",通过分片突破 MTU 限制,发送给接收方。换句话说,IP 层提供给传输层的抽象是数据包(packet,或者叫 TCP 分段 TCP segment)的传输能力。
4 传输层(用TCP举例)
4.1 简介
TCP 协议(传输控制协议/Transmission Control Protocol)是一种面向连接的、可靠的、基于字节流的传输层通信协议。TCP 协议中的知识点太多,本文只提及几个关键的部分。
4.2 传输过程
应用层向 TCP 层发送数据流。
TCP 层把数据流分割成适当长度的 packet/segment,IP层把 packet 发送给接收端的 TCP 层。
为了可靠传输,每个 packet 被分配了序号,接收端收到 packet 会回一个确认消息(ACK)。
如果发送端在一定时间(RTT)内未收到确认,发送端就会重传。
通过校验函数,TCP 层可以校验每个 packet 是否有错误,错误的 packet 也会重传。
因为 IP 层并不能保证 packet 的顺序到达,所以 TCP 接收端需要通过序号重组 packet。
图-8
4.3 MSS
IP 层并没有限制数据包(packet)的大小。但是基于性能和传输效率的考虑,TCP 层会基于 MTU 协商一个限制,那就是 MSS。
MSS:Maximum Segment Size ,最大分段大小(以字节为单位)。是传输层 TCP 协议范畴内的概念,用于标识 TCP 能够承载的最大应用数据段长度。是不包含 TCP Header 和 TCP Option 的 TCP 有效载荷(Payload)。
TCP 在三次握手过程中,会在 SYN 报文中使用 MSS 选项功能,协商交互双方能够接收的 MSS 值。交互双方会以双方通告的 MSS 值中取最小值作为发送报文的 MSS。因为 PMTU 在两个方向上不一定是一致的,MSS 也可能是不一致的。
大的 MSS,确实可以节省 TCP 的头信息,增加传输效率。
图-9
大的 MSS 同时会造成 IP 分片,降低 IP 层的传输效率。上面网络层举的例子,如果我们控制每个数据包的大小都小于1,480字节,那么用3个 IP 报文就可以完成4,300字节的数据的传输。图-10
所以推荐的 MSS = MTU - IP头 - TCP头。常见的基于以太网的协商结果:MSS=1500 - 20(IP头) - 20(TCP头) = 1460 byte。
4.4 流量控制
因为接收方需要重组 packet,并且 TCP 是面向 Stream 的,所以 TCP 重组比网络层的重组占用的内存还要大。
流量控制用来避免发送数据发送得过快而导致接收方来不及收下,所以由接收方通告给发送方进行调控。
TCP 使用滑动窗口协议实现流量控制。接收方在“接收窗口”域(接收缓冲剩余)指出还可接收的字节数量。发送方在没有新的确认包的情况下至多发送“接收窗口”允许的字节数量。接收方可修改“接收窗口”的值。
之所以叫滑动窗口,是因为根据接收窗口的大小,ack 的最大 packet 的序号,和 MSS,就可以确定可以发送的 packet 范围,这个范围是滑动变化的。并且发送顺序没有严格的要求。
图-11
4.5 拥塞控制
早期的 TCP 实现是没有拥塞窗口的。如果传输路径没有问题,定义好滑动窗口就可以控制好传输。如果传输路径有问题,比如带宽有限,承受不了这么多数据,那就会走到 TCP 的确认重传机制,这会进一步降低网络的负载能力。
所以 TCP 才提出了拥塞控制(拥塞窗口)。它一个应用就是 slow start。简单讲,发送方开始只发一小段数据,当收到了 ACK之后,再增加每次发送的数据,直到达到接收方的处理能力上限或者传输路径的负载能力上限。发送方每次发送的数据长度,就是拥塞窗口的大小。
4.6 抽象
数据在TCP层被抽象为数据包(packet,或者叫 TCP 分段 TCP segment)。
TCP 层提供给应用层的抽象称之为流(Stream),操作系统将 TCP 连接抽象为套接字(socket),作为编程接口给程序使用。
End