Microsoft Windows 操作系统使用基于 1978 年国际标准化组织 (ISO) 开发的七层网络模型的网络体系结构。
ISO 开放系统互连 (OSI) 参考模型将网络描述为“一系列协议层,具有分配给每个层的一组特定函数。 每个层都向更高层提供特定的服务,同时阻止这些层了解服务实现方式的详细信息。 每对相邻层之间定义完善的接口定义了由下层到较高层提供的服务以及访问这些服务的方式。”
下图演示了 OSI 模型:
Windows 网络驱动程序 实现 OSI 模型的底层四层。但是注意,windows本身对这四层的也分成了几个层:
物理层: 物理层是 OSI 模型的最低层。 此层管理非结构化原始位流通过物理介质的接收和传输。 它描述了与物理介质的电气/光学、机械和功能接口。 物理层承载所有较高层的信号。在 Windows 中,网络接口卡 (NIC) 实现物理层、其收发器和 NIC 连接到的介质。
数据链接层: 数据链接层在物理地址之间发送帧,负责物理层中发生的错误检测和恢复。数据链接层由电气和电子工程师协会 (IEEE) 进一步划分为两个子层:媒体访问控制 (MAC) 和逻辑链接控制 (LLC) 。
1. MAC: MAC 子层管理对物理层的访问、检查帧错误和管理接收帧的地址识别。在 Windows 网络体系结构中,MAC 子层在 NIC 中实现。 NIC 由称为微型端口驱动程序的软件设备 驱动程序控制。 Windows 支持微型端口驱动程序的多种变体,包括 WDM 微型端口驱动程序、微型端口调用管理器 (MFM) 和微型端口中间驱动程序。
2. LLC: LLC 子层提供从一个节点到另一个节点的数据帧的无错误传输。 LLC 子层建立和终止逻辑链接、控制帧流、序列帧、确认帧和重新传输未确认的帧。 LLC 子层使用帧确认和重新传输,通过指向上述层的链接提供几乎无错误的传输。在 Windows 中,称为 协议驱动程序的软件驱动程序 实现 LLC 子层。
网络层: 网络层控制子网的操作。 此层根据以下内容确定数据应采用的物理路径:
- 网络状况
- 服务优先级
- 其他因素,例如路由、流量控制、帧碎片和重新组装、逻辑到物理地址映射以及使用情况;
协议驱动程序实现网络层。
传输层: 传输层可确保按顺序传递消息,且不会丢失或重复。 此层使较高层协议免于担心与对等方进行数据传输。协议堆栈中需要最少的传输层,其中包括提供虚拟线路功能的可靠网络或 LLC 子层。 例如,由于适用于 Windows 的 NetBEUI 传输驱动程序是符合 OSI 的 LLC 子层,因此其传输层功能最少。 如果协议堆栈不包含 LLC 子层,并且网络层不可靠或支持与 TCP/IP 的 IP 层或 NWLink 的 IPX 层) 一样 数据报,则传输层应包括帧排序和确认,以及重新传输未确认的帧。在 Windows 网络体系结构中, 协议驱动程序(有时称为传输驱动程序)实现传输层。
综合下来我们可以看出,NDIS协议层驱动非常重要。
NDIS协议驱动简介
NDIS 协议驱动程序在其下边缘导出一组 ProtocolXxx 函数。 此类协议驱动程序与 NDIS 通信以发送和接收网络数据。 协议驱动程序绑定到在其上边缘导出 MiniportXxx 接口的基础微型端口 驱动程序或中间驱动程序。
中间驱动程序的微型端口驱动程序上边缘 (虚拟微型端口) 不管理物理设备。NDIS微型端口驱动程序管理物理设备。
协议驱动程序始终使用 NDIS 提供的函数来与基础 NDIS 驱动程序通信以发送和接收网络数据。 例如,具有无连接下边缘 (与无连接媒体的基础驱动程序通信的协议驱动程序(如以太网) )必须调用 NdisSendNetBufferLists 以将网络数据发送到基础 NDIS 驱动程序。 协议驱动程序可以调用 NdisOidRequest 来查询或设置基础无连接驱动程序支持的 OID。 具有面向连接的下边缘 (与面向连接的媒体的基础驱动程序通信的协议驱动程序必须调用 NdisCoSendNetBufferLists 以将网络数据发送到较低级别的 NDIS 驱动程序。 它还可以调用 NdisCoOidRequest 来查询或设置面向连接的基础驱动程序支持的 OID。
NDIS 还提供一组 NdisXxx 函数,用于隐藏基础操作系统的详细信息。 例如,协议驱动程序可以调用 NdisInitializeEvent 来创建用于同步的事件,并调用 NdisInitializeListHead 来创建链接列表。 使用此类函数的 NDIS 版本的协议驱动程序在 Microsoft 操作系统中更易于移植。 但是,协议驱动程序还可以调用内核模式支持例程,例如 IoCreateDevice。
初始化协议驱动程序
系统在加载驱动程序后调用协议驱动程序的 DriverEntry 例程。 协议驱动程序作为系统服务加载。 它们可以在微型端口驱动程序加载之前、期间或之后的任何时间加载。
协议驱动程序分配驱动程序资源并在 DriverEntry 中注册 ProtocolXxx 函数。 这包括 CoNDIS 客户端和独立的调用管理器。 若要将其 ProtocolXxx 函数注册到 NDIS,协议驱动程序会调用 NdisRegisterProtocolDriver 函数。
如果驱动程序成功注册为 NDIS 协议驱动程序,则 DriverEntry 返回STATUS_SUCCESS或其等效NDIS_STATUS_SUCCESS。 如果 DriverEntry 无法通过传播 由 NdisXxx 函数或内核模式支持例程返回的错误状态来初始化,驱动程序将不会保持加载状态。 DriverEntry 必须同步执行;也就是说,它不能返回STATUS_PENDING或其等效NDIS_STATUS_PENDING。
NDIS 协议驱动程序的 DriverEntry 函数必须调用 NdisRegisterProtocolDriver 函数。 若要向 NDIS 库注册驱动程序的 ProtocolXxx 入口点,协议驱动程序会初始化 NDIS_PROTOCOL_DRIVER_CHARACTERISTICS 结构并将其传递给 NdisRegisterProtocolDriver。
调用 NdisRegisterProtocolDriver 的驱动程序必须准备好立即调用其任何 ProtocolXxx 函数。
NDIS 协议驱动程序提供以下 ProtocolXxx 函数,这些函数是旧版驱动程序提供的函数的更新版本:
- ProtocolSetOptions
- ProtocolBindAdapterEx
- ProtocolUnbindAdapterEx
- ProtocolOpenAdapterCompleteEx
- ProtocolCloseAdapterCompleteEx
- ProtocolNetPnPEvent
- ProtocolUninstall
NDIS 协议驱动程序为发送和接收操作提供以下 ProtocolXxx 函数:
- ProtocolReceiveNetBufferLists
- ProtocolSendNetBufferListsComplete
所有类型的 NDIS 协议驱动程序都应注册功能齐全的 ProtocolBindAdapterEx 和 ProtocolUnbindAdapterEx 函数,以支持即插即用 (PnP) 。 通常, DriverEntry 函数应在返回状态值为 STATUS_SUCCESS 或 NDIS_STATUS_SUCCESS 的控件之前立即调用 NdisRegisterProtocolDriver 。
除 NDIS 定义的 ProtocolXxx 函数外,导出一组标准内核模式驱动程序例程的任何协议驱动程序都必须在传递给其 DriverEntry 函数的给定驱动程序对象中设置这些驱动程序例程的入口点。
如果尝试分配驱动程序执行网络 I/O 操作所需的资源失败, DriverEntry 应在返回状态为STATUS_SUCCESS或NDIS_STATUS_SUCCESS以外的控制之前释放它已分配的所有资源。
如果在成功调用 NdisRegisterProtocolDriver 后发生错误,驱动程序必须在 DriverEntry 返回之前调用 NdisDeregisterProtocolDriver 函数。
为了允许协议驱动程序配置可选服务,NDIS 在协议驱动程序调用 NdisRegisterProtocolDriver 的上下文中调用 ProtocolSetOptions 函数。
CoNDIS 客户端驱动程序必须从 ProtocolSetOptions 函数调用 NdisSetOptionalHandlers 函数。 驱动程序初始化NDIS_CO_CLIENT_OPTIONAL_HANDLERS结构,并在 NdisSetOptionalHandlers 的 OptionalHandlers 参数中传递它。
CoNDIS 独立调用管理器还必须从 ProtocolSetOptions 函数调用 NdisSetOptionalHandlers 函数。 驱动程序初始化NDIS_CO_CALL_MANAGER_OPTIONAL_HANDLERS结构,并在 NdisSetOptionalHandlers 的 OptionalHandlers 参数中传递它。
MMC 不是协议驱动程序。 因此,它们必须从 MiniportSetOptions 函数调用 NdisSetOptionalHandlers 函数。 MCM 初始化NDIS_CO_CALL_MANAGER_OPTIONAL_HANDLERS结构,并在 NdisSetOptionalHandlers 的 OptionalHandlers 参数中传递它。
若要注销 NDIS,协议驱动程序从其 Unload 例程调用 NdisDeregisterProtocolDriver。
若要在卸载协议驱动程序之前执行清理操作,协议驱动程序可以注册 ProtocolUninstall 函数。 ProtocolUninstall 函数是可选的。 例如,中间驱动程序的协议下边缘可能需要 ProtocolUninstall 函数。 在 NDIS 调用其 MiniportDriverUnload 函数之前,中间驱动程序可以在 ProtocolUninstall 中释放其协议边缘资源。
协议绑定状态和操作
对于驱动程序管理的每个绑定,NDIS 协议驱动程序必须支持以下操作状态:
- 未绑定:“未绑定”状态是绑定的初始状态。 在此状态下,协议驱动程序等待 NDIS 调用 ProtocolBindAdapterEx 函数;
- 打开:在“打开”状态下,协议驱动程序为绑定分配资源并尝试打开适配器;
- 运行:在“正在运行”状态下,协议驱动程序对绑定执行发送和接收处理;
- 关闭:在“关闭”状态下,协议驱动程序关闭对适配器的绑定,然后释放绑定的资源;
- 暂停:在暂停状态下,协议驱动程序完成停止绑定的发送和接收操作所需的任何操作。在“暂停”状态下,协议驱动程序不对绑定执行发送或接收操作;
- 重新启动:在“正在重启”状态下,协议驱动程序完成重启绑定的发送和接收操作所需的任何操作;
在下表中,标题表示绑定状态,事件列在第一列中。 表中的其余条目指定在某个状态内发生事件后绑定进入的下一个状态。 空条目表示无效的事件/状态组合:
注意 上表中列出的事件是 NDIS 协议绑定的主要事件。 当信息可用时,其他事件将添加到此表。
主要绑定事件的定义如下:
ProtocolBindAdapterEx:NDIS 调用驱动程序的 ProtocolBindAdapterEx 函数后,绑定进入“打开”状态。 有关详细信息,请参阅 绑定到适配器。
绑定失败:如果协议驱动程序无法绑定到适配器,绑定将返回到“未绑定”状态。
绑定已完成:如果驱动程序成功打开适配器,绑定将进入“已暂停”状态。 驱动程序完成绑定操作。
ProtocolUnbindAdapterEx:NDIS 调用驱动程序的 ProtocolUnbindAdapterEx hander 后,绑定进入 “关闭 ”状态。
取消绑定已完成:驱动程序完成取消绑定操作后,绑定将进入“未绑定”状态。
PnP 暂停:NDIS 向协议驱动程序发送网络即插即用 (PnP) 暂停事件通知后,绑定进入暂停状态。 有关详细信息 ,请参阅暂停绑定。
暂停已完成:驱动程序完成停止发送和接收操作所需的所有操作后,暂停操作完成,绑定处于“已暂停”状态。在暂停操作完成之前,驱动程序必须等待其所有未完成的发送请求完成。
PnP 重启:NDIS 向协议驱动程序发送网络 PnP 重启事件通知后,绑定将进入“正在重启”状态。 有关详细信息,请参阅 重启绑定。
重启已完成:驱动程序准备好处理发送和接收操作后,重启操作完成,绑定处于“正在运行”状态。
重启失败:如果 NDIS 向协议驱动程序发送网络 PnP 重启事件通知,并且重启尝试失败,则绑定将返回到“已暂停”状态。
发送和接收操作:协议驱动程序必须处理处于“正在运行”和“暂停”状态的发送和接收操作。
OID 请求:协议驱动程序可以启动 OID 请求,以在基础驱动程序中设置或查询信息。 协议驱动程序可以从所有状态(未绑定和打开)启动 OID 请求。