第 10 章 网络操作 --- 10.3 Windows的网络驱动堆叠
本节俯瞰 Windows 网络驱动的完整堆叠(stack)。 Windows 网络栈从下到上由 NIC Miniport → NDIS 库 → 协议驱动(LAN Manager)→ TCP/IP 协议栈 → TDI → AFD → Winsock 多层构成。ReactOS 的实现涉及 drivers/network/dd/(file:///d:/reactos/drivers/network/dd/)、drivers/network/lan/(file:///d:/reactos/drivers/network/lan/)、drivers/network/tcpip/(file:///d:/reactos/drivers/network/tcpip/)、drivers/network/afd/(file:///d:/reactos/drivers/network/afd/) 等多个模块。
概述
Windows 网络栈的设计哲学是 分层协议独立:
- 应用层 不直接接触硬件
- Winsock 提供 BSD 风格的 socket API
- AFD 把 socket API 转为内核操作
- TCP/IP 是众多协议之一,可被替换
- NDIS 抽象 NIC,多协议可共享
本节内容概览
- 10.3.0 框架图
- 10.3.1 NIC Miniport 驱动
- 10.3.2 LAN Manager(协议驱动)
- 10.3.3 TCP/IP 协议栈
- 10.3.4 AFD 与 Winsock
- 10.3.5 数据流:应用 → 硬件
- 10.3.6 数据流:硬件 → 应用
- 10.3.7 ReactOS 实现的特殊点
- 10.3.8 总结与代码索引
学习目标
- 理解 Windows 网络栈的完整层次
- 知道 NIC → LAN → TCP/IP → AFD → Winsock 的协作
- 跟踪一次 socket send 的完整路径
- 跟踪一次网络包接收的完整路径
涉及的内核子系统
| 子系统 | 头文件/源文件 | 核心作用 |
|---|---|---|
| NIC DD | drivers/network/dd/(file:///d:/reactos/drivers/network/dd/) | dc21x4、e1000、rtl8139 |
| NDIS | drivers/network/ndis/ndis/(file:///d:/reactos/drivers/network/ndis/ndis/) | NDIS 库 |
| LAN Manager | drivers/network/lan/lan/(file:///d:/reactos/drivers/network/lan/lan/) | NDIS 协议驱动 |
| TCP/IP | drivers/network/tcpip/tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) | lwIP + 协议栈 |
| AFD | drivers/network/afd/afd/(file:///d:/reactos/drivers/network/afd/afd/) | 内核 Winsock |
| Winsock DLL | dll/win32/ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) | Win32 API |
10.3.0 框架图
应用层
+-------------------------------------+
| Application (浏览器、FTP、...) |
+-------------------------------------+
| socket/send/recv
v
+-------------------------------------+
| Winsock DLL (ws2_32.dll + msafd) |
+-------------------------------------+
| DeviceIoControl (IRP)
v
+-------------------------------------+
| AFD (afd.sys) |
| - socket 内核对象 |
| - bind/connect/accept/listen |
| - select/异步通知 |
+-------------------------------------+
| TDI (IRP_MJ_*, IOCTL_TDI_*)
v
+-------------------------------------+
| TCP/IP (tcpip.sys) |
| - TCP / UDP / IP / ICMP / ARP |
| - 基于 lwIP |
+-------------------------------------+
| NdisSend/NdisReceive (NDIS Protocol API)
v
+-------------------------------------+
| LAN Manager (lan.sys) |
| - NDIS 协议驱动 |
+-------------------------------------+
| NdisMSend/NdisMIndicateReceive (NDIS Miniport API)
v
+-------------------------------------+
| NDIS 库 (ndis.sys) |
+-------------------------------------+
| Miniport 回调
v
+-------------------------------------+
| NIC Miniport Driver |
| (e1000.sys / dc21x4.sys / ...) |
+-------------------------------------+
| 寄存器 / DMA
v
+-------------------------------------+
| 网络硬件 (NIC) |
+-------------------------------------+
|
v
+-------------------------------------+
| 网络介质 (电缆/光纤/无线) |
+-------------------------------------+
10.3.1 NIC Miniport 驱动
角色
NIC Miniport 驱动是 网络栈的最底层,直接操作硬件:
- 配置 NIC(MAC 地址、中断、DMA)
- 发送数据包(构造 TX 描述符)
- 接收数据包(解析 RX 描述符)
- 处理中断
- 错误处理
典型 Miniport 驱动
ReactOS 自带几种 Miniport:
| 驱动 | 设备 | 来源 |
|---|---|---|
dc21x4.sys |
DEC 21x4x 系列(百兆网卡) | 原版 |
e1000.sys |
Intel PRO/1000(千兆) | Intel 原始 |
rtl8139.sys |
Realtek 8139(百兆) | 原版 |
ne2k_pci.sys |
NE2000 兼容 | 原版 |
pcnetnic.sys |
AMD PCnet | 原版 |
Miniport 入口
c
NTSTATUS NTAPI
DriverEntry(PDRIVER_OBJECT DriverObject, PUNICODE_STRING RegistryPath)
{
NDIS_MINIPORT_CHARACTERISTICS Chars = {0};
Chars.MajorNdisVersion = 5;
Chars.MinorNdisVersion = 0;
Chars.InitializeHandler = MyInit;
Chars.HaltHandler = MyHalt;
Chars.SendHandler = MySend;
Chars.TransferDataHandler = MyTransferData;
Chars.ResetHandler = MyReset;
// ...
NdisMRegisterMiniport(&Status, DriverObject, &Chars, sizeof(Chars));
return Status;
}
详见 10.3.1 节 dc21x4 实例分析。
10.3.2 LAN Manager(协议驱动)
角色
LAN Manager(lan.sys)是 NDIS 协议驱动,位于 TCP/IP 协议栈和 NDIS 之间:
- 注册为 NDIS 协议驱动
- 提供 TDI 接口 给上层(TCP/IP)
- 调用 NDIS 协议 API 发送/接收包
关键 API
LAN Manager 实现 NdisRegisterProtocol 把自己注册为 NDIS 协议驱动,然后通过 TDI 与 TCP/IP 通信。
lan.sys 的具体实现细节将在 10.3.2 节展开。
10.3.3 TCP/IP 协议栈
角色
tcpip.sys 是 真正的协议栈,实现:
- IP:分组转发、分片重组
- ICMP:ping、错误报告
- ARP:IP → MAC 解析
- TCP:可靠连接、流量控制
- UDP:无连接数据报
关键实现
ReactOS 的 TCP/IP 内部使用 lwIP(lightweight IP)。lwIP 是一个开源的 TCP/IP 协议栈实现,专为嵌入式系统设计但功能完整。
上层接口
TCP/IP 暴露 TDI(Transport Driver Interface) 给上层(AFD):
c
#define IOCTL_TDI_QUERY_ADDRESSES 0x00030004
#define IOCTL_TDI_CONNECT 0x00030008
#define IOCTL_TDI_DISCONNECT 0x0003000C
#define IOCTL_TDI_SEND_DATAGRAM 0x0003001A
// ... 等等
AFD 通过 CreateFileW 打开 \Device\Tcp / \Device\Udp 并通过 IOCTL 与 TCP/IP 通信。
详见 10.3.3 节。
10.3.4 AFD 与 Winsock
角色
AFD (Ancillary Function Driver,afd.sys)是 内核态 Winsock:
- 维护 socket 状态
- 实现 bind/connect/accept/send/recv
- 处理 select/异步通知
- 把 Winsock 请求翻译为 IRP 发给协议驱动
Winsock DLL (ws2_32.dll + msafd.dll)是 用户态 Winsock:
- 提供 BSD socket API
- 把 socket() 等调用转换为对
\Device\Afd的 IOCTL - 处理 WSARecv/WSASend 等异步操作
详见 10.3.4 节。
10.3.5 数据流:应用 → 硬件
一次 send() 调用的完整路径
c
// 1. 应用调用 send()
send(socket, buffer, length, 0);
用户态 (ws2_32.dll):
c
int send(SOCKET s, const char *buf, int len, int flags) {
WSABUF wsaBuf = {len, (char*)buf};
DWORD bytesSent;
return WSASend(s, &wsaBuf, 1, &bytesSent, flags, NULL, NULL);
}
int WSASend(SOCKET s, LPWSABUF bufs, DWORD bufCount, LPDWORD bytesSent,
DWORD flags, LPWSAOVERLAPPED ovl, LPWSAOVERLAPPED_COMPLETION_ROUTINE cr) {
// 构造请求
AFD_SEND_REQUEST req = { ... };
// 通过 msafd 翻译
return WSPStartup_table[s].WSPSend(s, bufs, bufCount, bytesSent, flags, ovl, cr);
}
msafd:
c
int WSPSend(SOCKET s, ...) {
// 打开 \Device\Afd 句柄(每个 socket 一个)
// 构造 AFD_SEND_INFO
// 调 NtDeviceIoControlFile 发 IOCTL_AFD_SEND
return NtStatus;
}
AFD (afd.sys):
c
NTSTATUS AfdSend(...) {
PAFD_SEND_INFO Info;
// 1. 验证 socket 状态
if (FCB->State != SOCKET_STATE_CONNECTED) {
return STATUS_INVALID_CONNECTION;
}
// 2. 构造 TDI 发送请求
TDI_REQUEST_KERNEL_SEND DatagramRequest;
// 3. 构造 IRP
PIRP Irp = TdiBuildInternalDeviceControlIrp(
TDI_SEND_DATAGRAM, // 或 TDI_SEND
TransportDeviceObject,
...);
// 4. 调用 TCP/IP
IoCallDriver(TcpDeviceObject, Irp);
}
TCP/IP (tcpip.sys):
c
// lwIP 的 tcp_write / udp_send
err_t err = tcp_write(tpcb, data, len, copy);
NDIS 协议 → Miniport:
c
NdisSend(&Status, BindingHandle, Packet);
Miniport:
c
NDIS_STATUS MySend(NDIS_HANDLE AdapterContext, PNDIS_PACKET Packet, UINT Flags) {
// 1. 从 Packet 提取数据
// 2. 构造 PRDT
// 3. 启动 DMA
return NDIS_STATUS_PENDING;
}
硬件:
NIC 把帧放到物理介质上。
10.3.6 数据流:硬件 → 应用
一次包接收的完整路径
硬件:
NIC 收到帧,触发中断。
Miniport ISR:
c
VOID MyInterrupt(NDIS_HANDLE MiniportInterruptContext) {
// 1. 读取中断状态寄存器
// 2. 识别是接收完成
// 3. 处理接收到的包
MyHandleReceive();
}
VOID MyHandleReceive() {
// 1. 分配包结构
PNDIS_PACKET Packet;
NdisAllocatePacket(&Status, &Packet, MyPacketPool);
// 2. 关联缓冲
NdisChainBufferAtFront(Packet, MyBuffer);
// 3. 通知 NDIS
NdisMIndicateReceivePacket(MyAdapterHandle, &Packet, 1);
}
NDIS(分发):
c
VOID NdisMIndicateReceivePacket(NDIS_HANDLE Adapter, PPNDIS_PACKET Packets, UINT Count) {
PNDIS_ADAPTER_BLOCK Adapter = ...;
// 对每个协议驱动调用 ReceivePacket
for (i = 0; i < Adapter->ProtocolCount; i++) {
Protocol = Adapter->Protocols[i];
Protocol->ReceivePacketHandler(ProtocolBindingContext, Packets[i]);
}
}
LAN Manager / TCP/IP:
c
INT LanReceivePacket(NDIS_HANDLE Context, PNDIS_PACKET Packet) {
// 1. 检查包类型
// 2. 交给协议栈(lwIP)
// 通过 TCP/IP 的内部队列传递给 lwIP
return tcp_input(...); // lwIP 函数
}
lwIP 内部:
c
err_t tcp_input(struct pbuf *p, struct netif *inp) {
// 1. 解析 TCP 头
// 2. 查找 PCB
// 3. 数据入队到 socket
}
AFD(wakeup):
TCP/IP 通过 TDI 事件通知 AFD,AFD 唤醒等待的 recv() 线程。
用户态:
c
recv(socket, buffer, length, 0);
// 之前阻塞的 recv 返回,数据已被复制到 buffer
10.3.7 ReactOS 实现的特殊点
lwIP 集成
ReactOS 的 TCP/IP 协议栈使用 lwIP 1.4.x 作为协议实现。集成方式:
- 内核线程 :
tcpip_thread在后台跑 lwIP - 消息队列 :
tcpip_input、tcpip_callback等 - 内存管理:使用 NT 内核内存分配器
AFD 实现
afd.sys 是 完全 ReactOS 实现,不依赖任何 Windows 内部结构。它实现:
- 完整 socket 状态机
- bind/connect/accept
- send/recv
- select
- WSAEventSelect、WSAAsyncSelect
简化点
- 不实现
msafd的 Service Provider 接口(仅 LSP 1) - 不实现 QOS / RSVP
- 不实现 Winsock 2 直连(WSARecvMSG 等部分高级功能)
- 不实现 IPv6(lwIP 已支持,ReactOS 未集成)
10.3.8 总结
关键要点:
- 网络栈分层:硬件 → Miniport → NDIS → LAN Manager → TCP/IP → AFD → Winsock → 应用
- NIC Miniport 驱动:直接操作硬件
- LAN Manager:NDIS 协议驱动 + TDI 接口
- TCP/IP 协议栈:基于 lwIP
- AFD:内核 Winsock
- Winsock DLL:用户态 API
- 一次 send :
send → WSASend → msafd → AFD → TCP/IP → NDIS → Miniport → 硬件 - 一次 receive :
硬件 → ISR → Miniport → NDIS → Protocol → TCP/IP → AFD → 应用
Windows 网络驱动堆叠的深度架构分析
Windows 网络驱动堆叠是操作系统设计中最精密的多层架构之一。ReactOS 对这一架构的完整复现,不仅需要对 Windows 内部机制的深入理解,还需要在工程实现上做出大量权衡。本节从架构层面深入分析网络驱动堆叠的设计原理和 ReactOS 的实现策略。
分层架构的设计原则
Windows 网络栈的分层设计遵循以下核心原则:
-
信息隐藏:每一层只暴露必要的接口给相邻层,内部实现完全隐藏。例如,Miniport 驱动不需要知道 TCP 协议的任何细节,TCP/IP 协议栈也不需要知道 NIC 硬件的寄存器布局。
-
接口标准化:NDIS 定义了 Miniport 接口标准,TDI 定义了传输层接口标准,Winsock 定义了用户态 API 标准。标准化使得不同厂商的组件可以互操作。
-
层间解耦:每一层可以独立替换。例如,可以将 TCP/IP 替换为 IPX/SPX,而不影响 Miniport 驱动;可以更换 NIC 硬件,而不影响上层应用。
-
数据流双向性:数据可以在栈中上下流动。发送方向自上而下,接收方向自下而上。每一层在发送时添加头部封装,在接收时剥离头部。
ReactOS 中的层间通信机制
在 ReactOS 中,层间通信主要通过两种机制实现:
-
函数调用(同层内) :同一层内的模块通过直接函数调用通信。例如,lwIP 内部的 TCP 模块调用 IP 模块的
ip4_output函数。 -
IRP(跨层) :跨层通信通过 I/O 请求包(IRP)实现。IRP 是 Windows 内核中所有 I/O 操作的核心数据结构。当用户态应用调用
send()时,ws2_32.dll构造一个 IRP,通过NtDeviceIoControlFile系统调用将 IRP 发送到 AFD 驱动。AFD 处理完自己的逻辑后,再构造新的 IRP 发送到 TCP/IP 驱动。
这种 IRP 链式传递的模式意味着一次 send() 调用可能在内核中产生多个 IRP,每个 IRP 对应一个层次的处理。这种设计的优点是每一层可以异步处理请求,缺点是 IRP 的创建和传递带来额外开销。
网络驱动堆叠的初始化顺序
系统启动时,网络驱动堆叠按照严格的顺序初始化:
-
NDIS 库初始化 :
ndis.sys的DriverEntry首先运行,初始化全局列表(ProtocolListHead、MiniportListHead、AdapterListHead)。 -
Miniport 驱动注册 :各个 NIC 驱动(如
dc21x4.sys、e1000.sys)的DriverEntry调用NdisMRegisterMiniport,将回调函数注册到 NDIS 库。 -
PnP 设备枚举:PnP 管理器枚举 PCI 总线上的网络设备,为每个设备创建物理设备对象(PDO)。
-
适配器初始化 :NDIS 为每个网络设备创建适配器实例,调用 Miniport 的
MiniportInitialize初始化硬件。 -
协议驱动注册 :TCP/IP(
tcpip.sys)的DriverEntry调用NdisRegisterProtocol注册为 NDIS 协议驱动。 -
协议绑定 :协议驱动通过
NdisOpenAdapter绑定到适配器,建立数据通路。 -
TDI 设备创建 :TCP/IP 创建
\Device\Tcp、\Device\Udp等设备对象,准备接收上层请求。 -
AFD 初始化 :
afd.sys的DriverEntry创建\Device\Afd设备对象。 -
Winsock 加载 :当应用首次调用
WSAStartup时,ws2_32.dll加载msafd.dll,建立 SPI 入口表。
数据包封装的层次模型
在网络栈中,数据从应用到硬件的发送过程中,每经过一层都会添加该层的头部封装:
应用数据(无头部)
↓ AFD 添加 socket 层信息
↓ TCP/IP 添加 TCP 头(20 字节)→ TCP 段
↓ TCP/IP 添加 IP 头(20 字节)→ IP 数据报
↓ NDIS/LAN 添加以太网头(14 字节)→ 以太网帧
↓ Miniport 添加前导码和 FCS → 物理帧
接收过程则相反,每层剥离对应的头部:
物理帧 → Miniport 剥离前导码和 FCS
→ 以太网帧 → NDIS 剥离以太网头
→ IP 数据报 → TCP/IP 剥离 IP 头
→ TCP 段 → TCP/IP 剥离 TCP 头
→ 应用数据 → AFD 传递给应用
在 ReactOS 中,这种封装/解封装通过 NDIS_PACKET 和 NDIS_BUFFER 链实现。每个 NDIS_PACKET 包含一个缓冲区链表,链表中的每个节点对应一层的头部或数据。这种设计避免了频繁的数据复制,提高了处理效率。
多协议共存的实现
Windows 网络栈支持多个协议同时绑定到同一个 NIC。例如,一个网卡可以同时绑定 TCP/IP 和 IPX/SPX 协议。当 NIC 收到一个帧时,NDIS 库将帧分发给所有绑定的协议,每个协议根据帧的类型字段决定是否接受。
在 ReactOS 中,这种分发由 NdisMIndicateReceivePacket 实现:
c
VOID NdisMIndicateReceivePacket(
NDIS_HANDLE NdisAdapterHandle,
PPNDIS_PACKET PacketArray,
UINT NumberOfPackets)
{
PNDIS_ADAPTER_BLOCK Adapter = NdisAdapterHandle;
// 遍历所有绑定的协议
for (i = 0; i < Adapter->ProtocolCount; i++) {
Protocol = Adapter->Protocols[i];
// 调用每个协议的接收回调
Protocol->ReceivePacketHandler(
Protocol->ProtocolBindingContext,
PacketArray[i]);
}
}
每个协议在接收回调中检查帧的以太网类型字段(如 0x0800 表示 IP,0x0806 表示 ARP),只处理属于自己的帧。
IRP 完成与异步通知
网络 I/O 的异步特性使得 IRP 完成机制变得复杂。当一个 recv() 调用没有数据可收时,AFD 不会立即完成 IRP,而是将其挂起,等待数据到达。数据到达时,TCP/IP 通过 TDI 事件通知 AFD,AFD 再完成挂起的 IRP。
ReactOS 中的 AFD 使用 FAST_MUTEX 保护端点状态,使用 KEVENT 实现线程等待和唤醒。这种设计确保了异步 I/O 的正确性,同时避免了忙等待带来的 CPU 浪费。
ReactOS 网络栈的性能特征
ReactOS 的网络栈在性能方面有以下特征:
-
内存复制次数 :一次
send()调用至少涉及 3 次内存复制(用户缓冲 → AFD 缓冲 → lwIP pbuf → DMA 缓冲)。Windows 通过使用 MDL(Memory Descriptor List)和直接 DMA 减少复制次数。 -
锁竞争:lwIP 的全局锁是性能瓶颈。在高并发场景下,多个线程同时发送数据会导致锁竞争。Windows 的 TCP/IP 栈使用更细粒度的锁(每个连接一个锁)来减少竞争。
-
中断处理:ReactOS 的 Miniport 驱动使用传统的中断处理方式。现代 Windows 使用消息信号中断(MSI/MSI-X),可以减少中断延迟和 CPU 开销。
与 Windows 网络栈的架构对比
| 特性 | Windows | ReactOS |
|---|---|---|
| 协议栈实现 | 专有实现 | lwIP 开源实现 |
| NDIS 版本 | 6.30+ | 5.1 |
| IPv6 | 完整支持 | 未启用 |
| 卸载技术 | LSO/RSS/Checksum | 不支持 |
| 过滤平台 | WFP | TDI 过滤 |
| 名字解析 | DNS Client 服务 | 简单实现 |
| QoS | DiffServ/802.1p | 不支持 |
ReactOS 的网络栈虽然在功能和性能上与 Windows 存在差距,但其架构设计完整地反映了 Windows 网络体系的核心思想。这种架构上的忠实复现,使得 ReactOS 能够运行大多数为 Windows 编写的网络应用程序。
本章代码索引
| 文件 | 内容 |
|---|---|
| dd/dc21x4(file:///d:/reactos/drivers/network/dd/dc21x4/) | DEC 21x4x NIC 驱动 |
| dd/e1000(file:///d:/reactos/drivers/network/dd/e1000/) | Intel PRO/1000 |
| dd/rtl8139(file:///d:/reactos/drivers/network/dd/rtl8139/) | Realtek 8139 |
| ndis/ndis/(file:///d:/reactos/drivers/network/ndis/ndis/) | NDIS 库 |
| lan/lan/(file:///d:/reactos/drivers/network/lan/lan/) | LAN Manager |
| tcpip/tcpip/(file:///d:/reactos/drivers/network/tcpip/tcpip/) | TCP/IP 协议栈 |
| afd/afd/(file:///d:/reactos/drivers/network/afd/afd/) | AFD 内核 Winsock |
| ws2_32/(file:///d:/reactos/dll/win32/ws2_32/) | 用户态 Winsock |
| msafd/(file:///d:/reactos/dll/win32/msafd/) | Microsoft Winsock |