1. 网络协议栈概述:从物理信号到应用数据
大家好,我是你们的讲师。今天咱们聊聊网络协议栈的总体框架。说实话,我做了十几年内核网络开发,每次带新人时,第一件事就是让他们把这张「地图」刻在脑子里。没有全局观,你调起 bug 来会非常痛苦。
1.1 OSI七层模型 vs TCP/IP四层模型
先看这张经典的对比图。很多教科书喜欢把 OSI 七层模型捧得很高,但实际干活的人都知道——TCP/IP 四层模型才是真家伙。OSI 更多是理论参考,TCP/IP 是实战标准。
| OSI 七层模型 | TCP/IP 四层模型 | 核心作用 |
|---|---|---|
| 应用层 | 应用层 | HTTP、FTP、DNS 等协议 |
| 表示层 | 数据加密、压缩、格式转换 | |
| 会话层 | 建立/管理/终止会话 | |
| 传输层 | 传输层 | TCP/UDP,端口到端口的通信 |
| 网络层 | 网络层 | IP 路由、寻址、分片 |
| 数据链路层 | 网络接口层 | MAC 地址、帧封装、差错检测 |
| 物理层 | 比特流传输、电气信号 |
我个人习惯把 OSI 当作「面试题」,把 TCP/IP 当作「吃饭工具」。你想想看,Linux 内核里哪有表示层和会话层的影子?它们被合并到应用层里了。说白了,OSI 是理想,TCP/IP 是现实。
核心要点:TCP/IP 四层模型是 Linux 内核网络协议栈的实现基础。每一层只关心自己的事,下层为上层服务。这种分层设计让内核代码变得非常优雅——当然,也带来了不少性能开销。
1.2 Linux网络协议栈架构总览
好,咱们把目光聚焦到 Linux 内核。下面这张图是我手绘的协议栈架构,你仔细看——数据从网卡进来,一路向上,经过层层处理,最后到达应用层。反过来也一样。
嗯,这里要注意——每一层之间的「接口」是固定的。传输层和网络层之间通过 struct sk_buff 传递数据,这个结构体是整个协议栈的「血液」。我在项目中遇到过不少性能问题,最后追根溯源,都是 sk_buff 的分配和拷贝太频繁了。
避坑指南:我曾经在调优一个网关设备时,发现吞吐量上不去。查了三天,最后发现是 sk_buff 的共享内存池配置太小,导致频繁的 kmalloc。把内存池调大后,性能直接翻倍。所以,sk_buff 的管理是协议栈优化的第一课。
1.3 数据包在内核中的旅程
咱们来走一遍数据包的完整旅程。从网卡收到一个比特流开始,到应用层读到数据,中间经历了什么?我习惯把这个过程分成 6 个关键步骤:
- 网卡接收:物理信号变成数字帧,DMA 写入内存
- 驱动层处理:NAPI 轮询,构造 sk_buff,调用
netif_receive_skb() - 数据链路层:检查 MAC 地址,剥掉以太网头,交给
ip_rcv() - 网络层处理:IP 校验、路由查找、分片重组,交给
tcp_v4_rcv() - 传输层处理:TCP 状态机、序列号检查、流量控制,放入 socket 接收队列
- 应用层读取:通过系统调用
recvfrom()或read()从 socket 缓冲区拷贝到用户空间
你想想看,一个简单的 HTTP 请求,在内核里要经过这么多道工序。每一道工序都可能成为瓶颈。我记得有一次排查一个视频流卡顿的问题,发现是网络层的路由缓存过期策略太激进,导致每个包都要查路由表——这就是典型的「配置不当引发的性能灾难」。
关键数据结构:整个旅程中,struct sk_buff 是贯穿始终的「快递包裹」。它包含了数据指针、协议头偏移、时间戳、设备信息等。你可以把它理解成一个带目录的集装箱——每一层只操作自己关心的那部分。
1.4 为什么理解这个架构很重要?
说白了,你以后调网络性能、写内核模块、甚至排查应用层超时问题,都得靠这张「地图」来定位。比如:
- 应用层超时,可能是传输层的重传超时设置不对
- 吞吐量上不去,可能是网络层的路由缓存满了
- 延迟忽高忽低,可能是数据链路层的 NAPI 轮询参数没调好
没有全局观,你就像在黑屋子里找开关——只能瞎摸。有了这张图,你至少知道「我现在在第几层,该往哪个方向查」。
注意:不要试图一次性搞懂所有细节。协议栈的代码量超过 50 万行,你不可能全记住。我的建议是:先搭框架,再填细节。遇到问题时,先定位到层,再深入具体函数。
好,这一章就到这里。记住这张架构图,后面的每一章都会围绕它展开。咱们下一章见。