Linux系统是如何收发网络包的
参考资源 小林coding 2022.3.29
OSI网络模型
解决不同设备网络互联中的兼容性问题 -> 解决不同设备在网络互联中的兼容性问题
国际标准化组织制定了 开放式系统互联通信参考模型
-> OSI网络模型
该模型一共有七层
- 应用层 负责给应用程序提供统一的接口
- 表示层 负责把数据转换成兼容另一个系统能识别的格式
- 会话层 负责建立 管理 终止表现层实体之间的通信会话
- 传输层 负责端到端的数据传输
- 网络层 负责数据的路由 转发 分片
- 数据链路层 负责数据的封帧 和 差错检测 以及 MAC寻址
- 物理层 负责在物理网络中 传输数据帧
OSI模型太过复杂 提出的也只是概念理论的分层 并没有提供具体的实现方式
常见
的是四层模型
即 TCP/IP网络模型 Linux系统正是根据这套网络模型来实现网络协议栈的
TCP/IP网络模型一共有四层
-
应用层 负责向用户提供一组应用程序 比如 HTTP DNS FTP
-
传输层 负责端到端的通信 比如TCP UDP等
-
网络层 负责网络包的封装 分片 路由 转发 比如IP ICMP等
-
网络接口层 负责网络包在物理网络中的传输 比如网络包的封帧、
MAC寻址 差错检查 以及通过网卡传输网络帧等
Linux网络协议栈
- 传输层 应用数据+TCP头
- 网络层 TCP数据包+ IP头
- 网络接口层 IP数据包前后增加了帧头和帧尾
最大传输单元MTU
1500字节 规定单词传输的最大IP包大小
网络包超过MTU -> 分片
- 应用程序通过系统调用 socket库进行数据交互
- socket层 之后 传输层 网络层 网络接口层
- 最后是 网卡驱动程序 和 网卡设备
Linux接受网络包的流程
网卡 -> 接收发送网络包的硬件 -> 网卡接收到网络包之后 ->通过DMA技术 将网络包放入缓冲区
接受网络包之后 -> 触发中断 -> 告知操作系统
问题 : 高性能网络场景 -> 网络包数量非常多 -> 触发非常多中断 -> 其他任务没办法执行了 ->寄了
解决问题 : Linux内核2.6版本 引入 NAPI机制
混合 终端和轮询
的方式接受网络包
核心概念: 不采用中断的方式读取数据 首先采用终端唤醒数据接受的服务程序 然后 poll的方法来轮询数据
比如,当有网络包到达时,网卡发起硬件中断,于是会执行网卡硬件中断处理函数,中断处理函数处理完需要「暂时屏蔽中断」,然后唤醒「软中断」来轮询处理数据,直到没有新数据时才恢复中断,这样一次中断处理多个网络包,于是就可以降低网卡中断带来的性能开销。
处理网络包 ->从缓冲区拷贝数据到内核 缓冲区中 -> 交给网络协议栈逐层处理
Linux发送网络包流程
与接受相反
应用程序 -> 调用socket发送接口 -> 用户态转变为内核态 -> 应用层数据拷贝至socket发送缓冲区
网络协议栈从socket取出数据包 根据TCP/IP协议栈逐层处理
如果使用的是 TCP 传输协议发送数据,那么会在传输层增加 TCP 包头,然后交给网络层,网络层会给数据包增加 IP 包,然后通过查询路由表确认下一跳的 IP,并按照 MTU 大小进行分片。
分片后的网络包,就会被送到网络接口层,在这里会通过 ARP 协议获得下一跳的 MAC 地址,然后增加帧头和帧尾,放到发包队列中。
这一些准备好后,会触发软中断告诉网卡驱动程序,这里有新的网络包需要发送,最后驱动程序通过 DMA,从发包队列中读取网络包,将其放入到硬件网卡的队列中,随后物理网卡再将它发送出去。
总结
OSI七层模型-> 过于繁琐 -> 简化为TCP/IP四层模型 (Linux网络栈实现)
四层 : 应用层 传输层 网络层 网络接口层
逐层从上到下解析 -> socket -> 网卡