NDIS 库 (ndis.sys) 驱动程序分析
目录
1. 概述
NDIS (Network Driver Interface Specification) 是微软定义的网络驱动接口规范,是 Windows 网络体系结构的核心组件。ReactOS 的 NDIS 库 (ndis.sys) 是一个遵循 NDIS 5.1 规范的实现,位于 d:\reactos\drivers\network\ndis\ 目录下。
1.1 设计思想与架构哲学
NDIS 的设计核心思想是分层抽象 + 集中调度。
在传统的网络驱动实现中,协议栈(如 TCP/IP)需要直接与硬件驱动交互,这要求每个协议驱动都必须了解每种网卡驱动的内部细节,导致严重的耦合问题。NDIS 的解决办法是引入一个中间抽象层——即 ndis.sys 本身——位于协议驱动和微型端口驱动之间,承担以下核心角色:
- 调度器角色:管理所有数据包的发送/接收序列化,通过工作项队列避免竞态条件
- 绑定器角色:维护协议→适配器、适配器→协议的动态绑定关系,支持即插即用热插拔
- 适配器角色:为上层的协议驱动和下层的微型端口驱动提供统一的函数调用约定,使两边都不需要知道对方的具体实现细节
这种设计的核心价值在于:微型端口驱动只需要实现 10-15 个标准回调函数(Init/Halt/Send/SendPackets/QueryInfo/SetInfo/Reset/ISR/HandleInterrupt/EnableInterrupt/DisableInterrupt/TransferData/ReturnPacket/CheckForHang),而协议驱动也只需要实现类似数量的回调(BindAdapter/UnbindAdapter/SendComplete/ReceiveHandler/StatusHandler/PnPEventHandler 等)。所有复杂的调度、同步、绑定管理、PnP 协调都由 NDIS 库统一处理。
1.2 主要功能
- 为协议驱动(如 TCP/IP)和微型端口驱动(如 e1000、rtl8139)提供标准化的通信接口
- 管理网络数据包的发送和接收流程
- 协调即插即用 (PnP) 事件和电源管理事件
- 提供配置管理(注册表操作)
- 提供内存管理(包括 DMA 共享内存)
- 提供计时器和同步原语
- 通过工作项队列实现序列化和异步调度
1.3 与 Linux 网络驱动架构的差异
ReactOS 的 TCP/IP 协议栈 (tcpip.sys) 实际上是从 Linux 内核 2.4.20 移植而来(见 [tcpcore.h](file:///d:/reactos/drivers/network/tcpip/include/tcpcore.h) 的注释 Ported from linux kernel 2.4.20),但在 NDIS 框架下运行时,两者呈现出截然不同的设计哲学。
| 对比维度 | Windows NDIS 设计 | Linux 网络驱动设计 |
|---|---|---|
| 核心架构 | 三层严格分层:协议驱动 → NDIS 库 → 微型端口驱动。NDIS 作为独立的内核模块存在,承担调度和管理职责 | 两层轻量架构:协议栈直接调用 net_device_ops 中的函数指针,没有独立的"中间库层",驱动模型由内核统一框架提供 |
| 数据包表示 | NDIS_PACKET + NDIS_BUFFER (MDL):基于 Windows MDL (Memory Descriptor List) 机制,包描述符和数据分离,通过 MDL 链支持 Scatter/Gather I/O | struct sk_buff:统一的缓冲区结构,使用 head/data/tail/end 四个指针灵活管理协议头空间,支持克隆 (skb_clone)、共享、分片 (skb_shinfo),内联了全套操作函数 |
| 发送模型 | 异步回调驱动:MiniportSend[Packets] 返回 NDIS_STATUS_PENDING 表示异步,发送完成后调用 NdisMSendComplete() 回调协议。NDIS 库负责任何必要的序列化和排队 | 同步+流量控制:dev->hard_start_xmit(skb) 同步发送,返回 NETDEV_TX_OK 或 NETDEV_TX_BUSY。驱动通过 netif_stop_queue()/netif_wake_queue() 控制上层是否继续发送 |
| 接收模型 | 推模式 (Push):微型端口通过 NdisMIndicateReceivePacket 将数据推送给 NDIS,NDIS 遍历绑定协议列表逐层推送。有传统 ReceiveHandler 和新式 ReceivePacketHandler 两种方式 | NAPI 轮询 (Poll)(Linux 2.6+):中断上半部关闭中断,注册 NAPI 结构到轮询列表;softIRQ 上下文中通过 poll(napi, budget) 批量轮询接收,减少高负载下的中断开销 |
| 协议绑定 | NDIS 集中管理:NDIS 维护全局 ProtocolListHead 和每个适配器的 ProtocolListHead,通过 ndisBindMiniportsToProtocol() 从注册表读取绑定配置。支持多个协议绑定到同一适配器 | 内核统一管理:通过 dev_add_pack() 注册 packet_type 到全局 ptype_all/ptype_base 哈希表,内核网络栈在接收时根据协议类型分发给不同处理函数 |
| 中断处理 | ISR + DPC 两阶段:MiniportISR(在 DIRQL 上运行,仅判断中断归属和是否需要 DPC)→ HandleDeferredProcessing DPC(在 DISPATCH_LEVEL 上运行,调用 MiniportHandleInterrupt + MiniportEnableInterrupt) | hardIRQ + softIRQ 两阶段:硬中断(仅做最小处理,如获取数据)→ softIRQ(NET_RX_SOFTIRQ/NET_TX_SOFTIRQ 中做实际协议处理,不在驱动中完成,而是交给内核协议栈) |
| 序列化模型 | 可配置序列化:通过 NDIS_ATTRIBUTE_DESERIALIZE 标志位区分串行化/反串行化两种模式。串行化模式下 NDIS 保证每次只有一个请求到达微型端口;反串行化模式下驱动自行管理并发 | 驱动自行管理:内核不提供统一的序列化机制,驱动使用自旋锁、RCU、per-CPU 变量等标准内核同步机制自行管理并发。ndo_start_xmit 可能在多个 CPU 上同时调用 |
| 配置/OID 管理 | 统一的 OID 机制:所有查询/设置操作通过 MiniportQueryInformation/MiniportSetInformation 一个回调入口,通过 NDIS_OID 枚举值(如 OID_GEN_MAC_OPTIONS、OID_802_3_CURRENT_ADDRESS)区分操作类型。NDIS 提供 NdisReadConfiguration、NdisOpenConfiguration 等注册表封装 | 分散的接口:通过多个 net_device_ops 函数实现(ndo_do_ioctl、ndo_get_stats、ndo_set_mac_address 等),或通过 ethtool_ops 实现。没有统一的 OID 枚举体系 |
| PnP/电源管理 | NDIS 内置支持:NDIS 通过 NdisIAddDevice(创建设备)、NdisIPnPStartDevice(启动设备,调用 MiniportInitialize)、NdisIPnPStopDevice(停止设备,调用 MiniportHalt)完整地封装了 PnP 流程。电源事件通过 NetEventSetPower/NetEventQueryPower PnP 事件通知协议 | 内核驱动模型:使用标准的 struct device_driver 模型,probe/remove 函数由内核在总线枚举时调用,电源管理通过 dev_pm_ops 实现 |
| DMA 管理 | NDIS 封装:NdisMInitializeScatterGatherDma 初始化 DMA 通道,NdisMAllocateSharedMemory 分配共享内存,GetScatterGatherList/PutScatterGatherList 管理 Scatter/Gather 列表。NDIS 在内部分配 DMA_ADAPTER 和 DMA_OPERATIONS | 通用 DMA API:dma_alloc_coherent()、dma_map_single()、dma_map_sg() 等。在 PCI 驱动中使用 pci_alloc_consistent() 等封装。由内核通用 DMA 层统一管理 |
| 兼容性 | NDIS 版本化兼容:通过编译宏 (NDIS40/NDIS50/NDIS51) 和不同大小的 MiniportCharacteristics 结构支持 NDIS 3.0/4.0/5.0/5.1 的二进制兼容。30stubs.c、40stubs.c、50stubs.c 提供桩函数 | 内核版本绑定:驱动必须针对特定内核版本编译,net_device_ops 结构在不同内核版本间有较大差异,不保证二进制兼容性 |
| 工作项延迟处理 | NDIS 工作项队列:MiniQueueWorkItem + MiniportWorker 实现串行化的工作项调度,支持 NdisWorkItemSend、NdisWorkItemRequest、NdisWorkItemResetRequested 等工作类型,通过内核 IoQueueWorkItem 实现 | workqueue/tasklet:Linux 内核提供通用的 work_struct + schedule_work() 和 tasklet 机制,驱动根据自己的需要使用这些通用机制,没有 NDIS 这样专门针对网络驱动的专用工作项队列 |
1.4 NDIS 这样设计的优缺点
优点:
- 驱动开发简化:微型端口驱动只需关注硬件操作本身,填写标准回调函数表即可。发送/接收的序列化、协议绑定管理、PnP 协调等复杂工作由 NDIS 代劳
- 二进制兼容性好:同一份微型端口驱动编译后在支持 NDIS 5.x 的 Windows 版本上均可运行,降低维护成本
- 统一的配置管理:通过 OID 机制和 NdisReadConfiguration 封装,驱动不需要直接与注册表打交道
- 版本平滑演进:通过不断扩展 MiniportCharacteristics 结构大小而不是修改函数签名,保持了向后的二进制兼容性
- 热插拔支持完善:PnP 事件经过 NDIS 统一调度后转发给协议驱动,确保所有绑定协议都能正确响应设备插拔
缺点:
- 性能开销:相比 Linux 的轻量模型,额外的 NDIS 层带来了函数调用开销和上下文切换(尤其在工作项队列场景)
- 灵活性受限:驱动必须在 NDIS 的框架内工作,无法绕过 NDIS 层直接操作协议栈
- 缺少 NAPI 等效的轮询机制:NDIS 5.1 仅支持中断驱动的推模式,高负载下可能产生大量中断(Linux NAPI 在 Linux 2.6 中引入以解决此问题)
- 依赖注册表:绑定配置和参数配置强依赖注册表,嵌入式场景下不够灵活
1.5 支持的 NDIS 版本
通过构建宏定义,支持 NDIS 3.0/4.0/5.0/5.1 的兼容性:
-DNDIS_WRAPPER
-DNDIS51
-DNDIS51_MINIPORT
-DNDIS_MINIPORT_DRIVER
-DNDIS_LEGACY_DRIVER=1
-DNDIS_LEGACY_MINIPORT=1
-DNDIS_LEGACY_PROTOCOL=1
2. 框架结构
2.1 分层架构
NDIS 采用标准的三层网络驱动架构:
+--------------------------------------+
| 协议驱动 (Protocol) |
| tcpip.sys, ppp.sys, etc. |
+--------------------------------------+
| NdisSend / NdisReceive etc.
v
+--------------------------------------+
| NDIS 库 (ndis.sys) |
| - 数据包调度 |
| - 协议/微型端口绑定管理 |
| - 配置管理 |
| - 中断处理 |
| - PnP/电源管理 |
+--------------------------------------+
| MiniportXxx callbacks
v
+--------------------------------------+
| 微型端口驱动 (Miniport) |
| e1000.sys, rtl8139.sys, etc. |
+--------------------------------------+
2.2 模块组织
NDIS 库由以下源文件组成:
| 源文件 | 功能 |
|---|---|
main.c | 驱动入口点 DriverEntry,初始化全局列表和锁 |
miniport.c | 微型端口支持(初始化、发送/接收完成、请求处理、工作项队列、PnP 事件处理) |
protocol.c | 协议驱动支持(协议注册、适配器打开/关闭、发送/请求转发) |
buffer.c | 数据包/缓冲区管理(NDIS_PACKET、NDIS_BUFFER 操作) |
config.c | 注册表配置读写(NdisOpenConfiguration、NdisReadConfiguration 等) |
memory.c | 内存管理(含 DMA 共享内存分配/释放) |
io.c | 中断服务例程 (ISR) 和 DPC 处理 |
hardware.c | PCI 配置空间读写、资源分配 |
control.c | 读写锁同步 |
efilter.c | 以太网帧过滤 |
object.c | NDIS 6.0 通用对象接口 |
workitem.c | NDIS 6.0 工作项接口 |
time.c | 计时器管理 |
string.c | 字符串操作 |
misc.c | 杂项(互锁操作、字符串比较) |
cl.c | 面向连接客户端 |
cm.c | 呼叫管理器 |
co.c | 面向连接支持 |
30stubs.c | NDIS 3.0 兼容性桩函数 |
40stubs.c | NDIS 4.0 兼容性桩函数 |
50stubs.c | NDIS 5.0 兼容性桩函数 |
2.3 关键调用约定
协议驱动 -> NdisSend() -> ProSend() -> proSendPacketToMiniport() -> MiniportSend[Packets]Handler
协议驱动 -> NdisRequest() -> ProRequest() -> MiniDoRequest() -> MiniportQueryInfo/SetInfoHandler
微型端口 -> NdisMSendComplete() -> MiniSendComplete() -> ProtocolSendCompleteHandler
微型端口 -> NdisMIndicateStatus() -> MiniStatus() -> ProtocolStatusHandler
3. 核心数据结构
3.1 逻辑适配器 (LOGICAL_ADAPTER)
定义在 [miniport.h](file:///d:/reactos/drivers/network/ndis/include/miniport.h#L87-L99):
typedef struct _LOGICAL_ADAPTER
{
NDIS_MINIPORT_BLOCK NdisMiniportBlock; // NDIS 定义的标准字段
PNDIS_MINIPORT_WORK_ITEM WorkQueueHead; // 工作队列头
PNDIS_MINIPORT_WORK_ITEM WorkQueueTail; // 工作队列尾
LIST_ENTRY ListEntry; // 全局适配器链表
LIST_ENTRY MiniportListEntry; // 微型端口驱动链表的入口
LIST_ENTRY ProtocolListHead; // 绑定到此适配器的协议链表
ULONG MediumHeaderSize; // 介质头大小(如 Ethernet=14)
HARDWARE_ADDRESS Address; // 硬件地址 (MAC)
ULONG AddressLength; // 硬件地址长度
PMINIPORT_BUGCHECK_CONTEXT BugcheckContext; // 关机回调上下文
} LOGICAL_ADAPTER, *PLOGICAL_ADAPTER;
3.2 微型端口驱动块 (NDIS_M_DRIVER_BLOCK)
typedef struct _NDIS_M_DRIVER_BLOCK {
LIST_ENTRY ListEntry; // 全局链表入口
KSPIN_LOCK Lock; // 保护自旋锁
NDIS_MINIPORT_CHARACTERISTICS MiniportCharacteristics; // 微型端口特性(函数指针表)
WORK_QUEUE_ITEM WorkItem; // 工作项
PDRIVER_OBJECT DriverObject; // 驱动对象
LIST_ENTRY DeviceList; // 该驱动创建的适配器列表
PUNICODE_STRING RegistryPath; // 注册表路径
} NDIS_M_DRIVER_BLOCK;
3.3 协议绑定 (PROTOCOL_BINDING)
定义在 [protocol.h](file:///d:/reactos/drivers/network/ndis/include/protocol.h#L10-L16):
typedef struct _PROTOCOL_BINDING {
LIST_ENTRY ListEntry; // 全局链表入口
KSPIN_LOCK Lock; // 保护自旋锁
NDIS_PROTOCOL_CHARACTERISTICS Chars; // 协议特性(函数指针表)
WORK_QUEUE_ITEM WorkItem; // 工作项
LIST_ENTRY AdapterListHead; // 已绑定的适配器列表
} PROTOCOL_BINDING, *PPROTOCOL_BINDING;
3.4 适配器绑定 (ADAPTER_BINDING)
typedef struct _ADAPTER_BINDING {
NDIS_OPEN_BLOCK NdisOpenBlock; // NDIS 定义的标准字段
LIST_ENTRY ListEntry; // 全局链表入口
LIST_ENTRY ProtocolListEntry; // 协议绑定的适配器链表入口
LIST_ENTRY AdapterListEntry; // 逻辑适配器的协议链表入口
KSPIN_LOCK Lock; // 保护自旋锁
PPROTOCOL_BINDING ProtocolBinding; // 对应的协议
PLOGICAL_ADAPTER Adapter; // 对应的适配器
} ADAPTER_BINDING, *PADAPTER_BINDING;
3.5 工作项 (NDIS_MINIPORT_WORK_ITEM)
支持的工作项类型:
| 工作项类型 | 用途 |
|---|---|
NdisWorkItemSend | 发送数据包 |
NdisWorkItemSendLoopback | 回环发送 |
NdisWorkItemReturnPackets | 归还数据包 |
NdisWorkItemResetRequested | 适配器重置 |
NdisWorkItemResetInProgress | 重置进行中 |
NdisWorkItemMiniportCallback | 微型端口回调 |
NdisWorkItemRequest | 查询/设置请求 |
NdisMaxWorkItems | 用于遍历所有类型 |
3.6 全局链表结构
全局协议列表 (ProtocolListHead)
↓
+-------------------+
| PROTOCOL_BINDING | ---- AdapterListHead --> [ADAPTER_BINDING] -> [ADAPTER_BINDING]
+-------------------+
全局微型端口列表 (MiniportListHead)
↓
+-------------------+
| NDIS_M_DRIVER_BLOCK| ---- DeviceList --> [LOGICAL_ADAPTER] -> [LOGICAL_ADAPTER]
+-------------------+
全局适配器列表 (AdapterListHead)
↓
+-------------------+
| LOGICAL_ADAPTER | ---- ProtocolListHead --> [ADAPTER_BINDING] -> [ADAPTER_BINDING]
+-------------------+
4. 核心模块分析
4.1 主入口 (main.c)
DriverEntry 函数执行以下初始化:
- 初始化全局协议列表
ProtocolListHead和对应的自旋锁 - 初始化全局微型端口列表
MiniportListHead和对应的自旋锁 - 初始化全局适配器列表
AdapterListHead和对应的自旋锁 - 注册卸载处理函数
MainUnload - 初始化取消 ID 计数器
4.2 微型端口管理 (miniport.c)
这是 NDIS 库中最大的模块,负责:
4.2.1 微型端口注册 (NdisMRegisterMiniport)
- 验证
MiniportCharacteristics版本(3.0/4.0/5.0/5.1) - 验证必需的函数指针(Halt, Initialize, Reset 必须提供)
- 复制特性到全局结构
- 注册
AddDevice回调 (NdisIAddDevice) - 注册通用 IRP 处理器 (
NdisGenericIrpHandler)
4.2.2 PnP 设备管理
设备创建 (NdisIAddDevice):
- 从注册表读取 Export 名称
- 调用
IoCreateDevice创建FILE_DEVICE_PHYSICAL_NETCARD设备 - 初始化
LOGICAL_ADAPTER结构 - 注册网络设备接口 (
GUID_DEVINTERFACE_NET) - 附加到设备栈
- 初始化计时器和 DPC(用于检测挂起)
设备启动 (NdisIPnPStartDevice):
- 将适配器加入全局列表
- 打开注册表键,读取总线类型/编号/槽号
- 设置回调处理函数
- 调用
MiniportInitialize - 查询 MAC 选项、硬件地址、lookahead 大小、最大发送包数
- 创建以太网过滤器 (
EthCreateFilter) - 启动挂起检测计时器
- 绑定所有协议到新适配器
设备停止 (NdisIPnPStopDevice):
- 从全局列表移除适配器
- 取消计时器
- 调用
MiniportHalt - 释放资源(内存、过滤器)
4.2.3 工作项队列
NDIS 使用工作项队列来序列化对微型端口的操作:
MiniQueueWorkItem() → 入队(尾插法)
MiniDequeueWorkItem() → 出队(头删法)
MiniportWorker() → 工作线程处理
MiniWorkItemComplete() → 完成并检查是否有后续工作项
工作类型包括发送、请求、重置等,通过 MiniportWorker 统一调度。
4.3 协议管理 (protocol.c)
4.3.1 协议注册 (NdisRegisterProtocol)
- 验证协议版本/特性
- 分配
PROTOCOL_BINDING结构 - 复制特性(含大写化协议名)
- 调用
ndisBindMiniportsToProtocol绑定适配器 - 发送
NetEventBindsCompletePnP 事件 - 加入全局协议列表
4.3.2 协议-适配器绑定 (ndisBindMiniportsToProtocol)
绑定过程:
- 构造注册表路径
\Registry\Machine\...\Services\{Protocol}\Linkage - 读取
Bind值(REG_MULTI_SZ格式) - 遍历每个设备名,检查是否已启动和是否已绑定
- 构造每适配器的参数路径
- 调用协议驱动的
BindAdapterHandler
4.3.3 适配器打开 (NdisOpenAdapter)
- 通过名称查找适配器 (
MiniLocateDevice) - 检查介质类型匹配
- 分配
ADAPTER_BINDING结构 - 设置处理函数(Request, Reset, Send, SendPackets, TransferData)
- 将绑定加入协议和适配器的链表
4.4 缓冲区管理 (buffer.c)
NDIS 缓冲区体系基于 MDL (Memory Descriptor List):
NDIS_PACKET
├── Private.Head → NDIS_BUFFER (MDL) → NDIS_BUFFER (MDL) → ...
├── Private.Tail → NDIS_BUFFER (MDL)
├── Reserved[0..3] (协议/微型端口保留)
├── WrapperReserved[0..3]
└── 包扩展数据 (NDIS_PACKET_OOB_DATA + NDIS_PACKET_EXTENSION)
关键操作:
NdisAllocateBuffer→ 调用IoAllocateMdl+MmBuildMdlForNonPagedPoolNdisAllocatePacketPoolEx→ 预分配池,通过空闲链表管理CopyBufferChainToBuffer→ 遍历 MDL 链复制到连续缓冲区CopyPacketToBuffer→ 从数据包复制到缓冲区
4.5 中断处理 (io.c)
中断处理分为两个阶段:
硬件中断
↓
MiniportISR() ← ISR 阶段(高 IRQL)
├── InterruptRecognized = TRUE
└── QueueMiniportHandleInterrupt = TRUE
↓
HandleDeferredProcessing() ← DPC 阶段(DISPATCH_LEVEL)
├── MiniportHandleInterrupt()
└── MiniportEnableInterrupt()
4.6 配置管理 (config.c)
NDIS 对注册表的操作提供了资源跟踪机制:
NdisOpenConfiguration/NdisOpenProtocolConfiguration→ 打开注册表键NdisReadConfiguration→ 读取配置值(支持 Integer/HexInteger/String/MultiString/Binary)NdisWriteConfiguration→ 写入配置值NdisCloseConfiguration→ 关闭并释放所有跟踪资源
资源跟踪通过 MINIPORT_CONFIGURATION_CONTEXT 和 MINIPORT_RESOURCE 链表实现。
4.7 以太网过滤 (efilter.c)
当微型端口指示接收到数据时,EthFilterDprIndicateReceive 被调用,它会:
- 检查以太网过滤器
- 调用
MiniIndicateData将数据传递给绑定的协议
5. 工作流程图
5.1 驱动初始化流程
5.2 数据包发送流程
5.3 数据包接收流程
5.4 请求处理流程 (OID 查询/设置)
5.5 适配器重置流程
6. 数据流图
6.1 发送数据流
协议驱动 NDIS 库 微型端口驱动
+--------+ +-----------+ +------------+
| TCP/IP | | ndis.sys | | e1000.sys |
+--------+ +-----------+ +------------+
| | |
|--- NdisSend(Packet) ---------->| |
| | |
| +-----+-----+ |
| | ProSend() | |
| +-----+-----+ |
| | |
| +-----v-----+ |
| | 检查回环 |---如果目标=本机MAC--+ |
| +-----+-----+ | |
| | | |
| +-----v-----+ v |
| | DMA? |---是--> Scatter/Gather |
| +-----+-----+ | |
| | | |
| +-----v-----+ | |
| | 发送到 | | |
| | 微型端口 |<------------------+ |
| +-----+-----+ |
| | |
| +-----v-----+ |
| | 序列化/ | |
| | 工作项队列 | |
| +-----+-----+ |
| | |
| +------- MiniportSend --------->|
| | (或 SendPackets) |
| | |
| | +---v---+
| | | 硬件 |
| | | 发送 |
| | +---v---+
| | |
| |<------- NdisMSendComplete ---+
| | |
| +-----v-----+ |
| |MiniSendComplete |
| +-----+-----+ |
| | |
|<------ SendCompleteHandler ----+ |
6.2 接收数据流
硬件 NDIS 库 协议驱动
+--------+ +-----------+ +--------+
| 网卡 | | ndis.sys | | TCP/IP |
+--------+ +-----------+ +--------+
| | |
|--- 硬件中断 ------------------>| |
| | |
| +-----v-----+ |
| | MiniportISR| |
| +-----+-----+ |
| | |
| +-----v-----+ |
| | DPC 处理 | |
| +-----+-----+ |
| | |
|<-- MiniportHandleInterrupt ----+ |
| | |
|--- MiniportTransferData[可选] ->| |
| (或 NdisMIndicateReceivePacket)| |
| | |
| +-----v-----+ |
| | 以太网 | |
| | 过滤器检查 | |
| +-----+-----+ |
| | |
| +-----v------+ |
| | 遍历协议列表 | |
| +-----+------+ |
| | |
| +------- ReceiveHandler -------->|
| | (或 ReceivePacketHandler)|
| | |
| | +---v---+
| | | 协议 |
| | | 处理 |
| | | 数据 |
| | +---v---+
| | |
|<--- NdisReturnPackets ---------+<--- 协议归还数据包 ------------+
6.3 OID 查询/设置数据流
协议驱动 NDIS 库 微型端口驱动
+--------+ +-----------+ +------------+
| TCP/IP | | ndis.sys | | e1000.sys |
+--------+ +-----------+ +------------+
| | |
|--- NdisRequest(Request) ------>| |
| (OID_GEN_xxx...) | |
| +-----v-----+ |
| | ProRequest| |
| +-----+-----+ |
| | |
| +-----v------+ |
| | MiniDoRequest| |
| +-----+------+ |
| | |
| +-----v------+ |
| | 根据请求类型 | |
| +-----+------+ |
| | |
| +------------+-------------+ |
| | | |
| +--------v------+ +--------v------+ |
| |QueryInfoHandler| |SetInfoHandler | |
| +--------+------+ +--------+------+ |
| | | |
| +------ 微型端口执行 -------+--> [硬件查询] |
| | (OID 处理) | |
| | | |
| +------ NdisM[Query/Set] --+ |
| | InformationComplete| |
| | | |
| +----------v----------+ |
| | MiniRequestComplete | |
| +----------+----------+ |
| | |
|<--------- RequestCompleteHandler --+ |
7. 初始化序列
7.1 完整的系统启动-网络初始化序列
Phase 1: NDIS 自身初始化
=========================
1. 系统加载 ndis.sys
2. DriverEntry() 调用
- 初始化 ProtocolListHead, MiniportListHead, AdapterListHead
- 初始化对应的自旋锁
Phase 2: 微型端口驱动加载
=========================
3. 系统加载 e1000.sys 等微型端口驱动
4. DriverEntry 调用:
a. NdisMInitializeWrapper() - 在全局 MiniportListHead 注册驱动块
b. NdisMRegisterMiniport() - 注册微型端口特性
- 验证函数指针
- 注册 AddDevice = NdisIAddDevice
- 注册 MajorFunction = NdisGenericIrpHandler
Phase 3: PnP 枚举设备
=====================
5. PnP 管理器枚举 PCI 总线上的网络设备
6. NdisIAddDevice() 调用:
a. 读取注册表 Export 名称
b. IoCreateDevice() 创建设备对象
c. 初始化 LOGICAL_ADAPTER
d. 注册网络设备接口
e. IoAttachDeviceToDeviceStack() 附加到设备栈
f. 初始化挂起检测 DPC 和计时器
7. IRP_MN_START_DEVICE 到达:
a. NdisIPnPStartDevice() 调用
b. 打开注册表读取配置
c. 设置内部处理函数钩子
d. 调用 MiniportInitialize() - 微型端口初始化硬件
e. 查询 MAC 能力 (OID_GEN_MAC_OPTIONS, OID_802_3_CURRENT_ADDRESS 等)
f. 创建以太网过滤器
g. 启动挂起检测计时器 (默认每2秒检查)
h. 绑定所有已注册协议到本适配器
Phase 4: 协议驱动加载
=====================
8. 系统加载 tcpip.sys 等协议驱动
9. DriverEntry 调用:
a. NdisRegisterProtocol() - 注册协议特性
b. ndisBindMiniportsToProtocol() - 读取 Linkage\Bind 键
c. 对每个适配器调用 BindAdapterHandler
d. BindAdapterHandler 调用 NdisOpenAdapter()
- MiniLocateDevice() 查找适配器
- 检查介质类型
- 分配 ADAPTER_BINDING
- 建立双向链表关系
Phase 5: 协议就绪
=================
10. NdisCompleteBindAdapter() 确认绑定完成
11. 协议驱动发送 NetEventBindsComplete PnP 事件
12. 网络协议栈开始正常收发数据
8. 关键函数分析
8.1 发送路径关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
NdisSend | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L1272-L1285) | 协议入口,委托给 ProSend |
ProSend | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L472-L574) | 检查回环 → DMA → 调用 proSendPacketToMiniport |
proSendPacketToMiniport | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L388-L470) | 检查繁忙→序列化/非序列化→调用微型端口 |
MiniSendComplete | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L529-L579) | 释放 Scatter/Gather 列表→回调协议SendCompleteHandler |
MiniQueueWorkItem | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L980-L1046) | 尾部插入工作项 |
MiniportWorker | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L1222-L1379) | 工作线程调度器,处理各种工作类型 |
8.2 接收路径关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
MiniIndicateData | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L175-L251) | 遍历协议列表,调用每个协议的 ReceiveHandler |
MiniIndicateReceivePacket | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L292-L432) | 处理 Packet 数组指示,管理引用计数 |
NdisReturnPackets | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L256-L290) | 递减引用计数,减到0时调用 ReturnPacketHandler |
EthFilterDprIndicateReceive | [efilter.c](file:///d:/reactos/drivers/network/ndis/ndis/efilter.c#L69-L99) | 以太网接收到入口,过滤后传递到 MiniIndicateData |
8.3 请求处理关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
NdisRequest | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L1239-L1252) | 协议入口,委托给 ProRequest |
ProRequest | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L307-L345) | 填充 MacBlock → 检查繁忙→MiniDoRequest |
MiniDoRequest | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L1106-L1179) | 分发到 QueryInformation/SetInformation/CoRequest Handler |
MiniQueryInformation | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L804-L862) | 同步/异步包装器,通过事件等待完成 |
MiniRequestComplete | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L480-L527) | 请求完成处理,通知协议或信号事件 |
8.4 注册和绑定关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
NdisMRegisterMiniport | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L2709-L2861) | 注册微型端口特性 |
NdisRegisterProtocol | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L1112-L1232) | 注册协议特性并绑定适配器 |
NdisOpenAdapter | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L762-L874) | 协议打开一个适配器建立绑定 |
ndisBindMiniportsToProtocol | [protocol.c](file:///d:/reactos/drivers/network/ndis/ndis/protocol.c#L906-L1107) | 将协议绑定到所有适配器 |
MiniLocateDevice | [miniport.c](file:///d:/reactos/drivers/network/ndis/ndis/miniport.c#L689-L753) | 按名称在全局列表查找适配器 |
8.5 缓冲区管理关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
NdisAllocatePacketPoolEx | [buffer.c](file:///d:/reactos/drivers/network/ndis/ndis/buffer.c#L447-L521) | 分配数据包池,创建空闲链表 |
NdisDprAllocatePacketNonInterlocked | [buffer.c](file:///d:/reactos/drivers/network/ndis/ndis/buffer.c#L683-L728) | 无锁分配,从空闲链表取一个 |
NdisAllocateBuffer | [buffer.c](file:///d:/reactos/drivers/network/ndis/ndis/buffer.c#L336-L364) | 分配 MDL 作为 NDIS 缓冲区 |
NdisGetFirstBufferFromPacket | [buffer.c](file:///d:/reactos/drivers/network/ndis/ndis/buffer.c#L871-L910) | 遍历数据包获取第一个缓冲区和总长度 |
CopyBufferChainToBuffer | [buffer.c](file:///d:/reactos/drivers/network/ndis/ndis/buffer.c#L113-L169) | 从 MDL 链复制到连续缓冲区 |
8.6 中断处理关键函数
| 函数 | 位置 | 功能 |
|---|---|---|
ServiceRoutine | [io.c](file:///d:/reactos/drivers/network/ndis/ndis/io.c#L49-L79) | ISR,调用 MiniportISR 判断中断来源 |
HandleDeferredProcessing | [io.c](file:///d:/reactos/drivers/network/ndis/ndis/io.c#L16-L47) | DPC,调用 MiniportHandleInterrupt+EnableInterrupt |
9. 构建配置
9.1 CMakeLists.txt
add_definitions(
-DNDIS_WRAPPER
-DNDIS51
-DNDIS51_MINIPORT
-DNDIS_MINIPORT_DRIVER
-DNDIS_LEGACY_DRIVER=1
-DNDIS_LEGACY_MINIPORT=1
-DNDIS_LEGACY_PROTOCOL=1)
add_library(ndis MODULE ...)
set_module_type(ndis kernelmodedriver)
add_importlibs(ndis ntoskrnl hal)
9.2 输出
编译结果为 reactos/system32/drivers/ndis.sys,链接 ntoskrnl 和 hal。
9.3 版本声明
NDIS_VERSION = 0x00050001 (= NDIS 5.1)
附录 A:NDIS 协议驱动特性结构
typedef struct _NDIS_PROTOCOL_CHARACTERISTICS {
UCHAR MajorNdisVersion; // NDIS 主版本
UCHAR MinorNdisVersion; // NDIS 次版本
USHORT Reserved; // 保留
NDIS_STRING Name; // 协议名称
OPEN_ADAPTER_COMPLETE_HANDLER OpenAdapterCompleteHandler;
CLOSE_ADAPTER_COMPLETE_HANDLER CloseAdapterCompleteHandler;
SEND_COMPLETE_HANDLER SendCompleteHandler;
TRANSFER_DATA_COMPLETE_HANDLER TransferDataCompleteHandler;
RECEIVE_HANDLER ReceiveHandler;
RECEIVE_COMPLETE_HANDLER ReceiveCompleteHandler;
STATUS_HANDLER StatusHandler;
STATUS_COMPLETE_HANDLER StatusCompleteHandler;
BIND_ADAPTER_HANDLER BindAdapterHandler;
UNBIND_ADAPTER_HANDLER UnbindAdapterHandler;
PNP_EVENT_HANDLER PnPEventHandler; // NDIS 5.0
RECEIVE_PACKET_HANDLER ReceivePacketHandler; // NDIS 5.0
SEND_COMPLETE_HANDLER SendCompleteHandler;
...
} NDIS_PROTOCOL_CHARACTERISTICS;
附录 B:NDIS 微型端口驱动特性结构
typedef struct _NDIS_MINIPORT_CHARACTERISTICS {
UCHAR MajorNdisVersion;
UCHAR MinorNdisVersion;
USHORT Filler;
UINT Reserved;
CHECK_FOR_HANG_HANDLER CheckForHangHandler;
DISABLE_INTERRUPT_HANDLER DisableInterruptHandler;
ENABLE_INTERRUPT_HANDLER EnableInterruptHandler;
HALT_HANDLER HaltHandler;
HANDLE_INTERRUPT_HANDLER HandleInterruptHandler;
INITIALIZE_HANDLER InitializeHandler;
ISR_HANDLER ISRHandler;
QUERY_INFORMATION_HANDLER QueryInformationHandler;
RESET_HANDLER ResetHandler;
SEND_HANDLER SendHandler; // NDIS 3.0/4.0
SET_INFORMATION_HANDLER SetInformationHandler;
TRANSFER_DATA_HANDLER TransferDataHandler;
RETURN_PACKET_HANDLER ReturnPacketHandler; // NDIS 4.0
SEND_PACKETS_HANDLER SendPacketsHandler; // NDIS 4.0
...
} NDIS_MINIPORT_CHARACTERISTICS;
附录 C:参考文档
- NDIS 5.1 规范 (MSDN)
- [ddk/ndis.h](file:///d:/reactos/sdk/include/ddk/ndis.h) — NDIS DDK 头文件
- [ddk/ndisguid.h](file:///d:/reactos/sdk/include/ddk/ndisguid.h) — NDIS GUID 定义
- [psdk/ntddndis.h](file:///d:/reactos/sdk/include/psdk/ntddndis.h) — NDIS IOCTL 定义
4802

被折叠的 条评论
为什么被折叠?



