图26显示了无连接网络程序的NDS环境。 (图2.6无连接网络驱动程序环境) 无连接环境是为无连接介质例如 Ethernet和 Token Ring所提供的标准的网络驱动程序 环境。参见22节中对这种环境下的驱动程序的描述 242面向连接环境下的网络驱动程序 NDS支持以下几种面向连接的驱动程序: 面向连接的微端口 ·面向连接的客户方 呼叫管理器 ·集成的微端口呼叫管理器(MCM 图27显示了一个面向连接的客户方,一个呼叫管理器,和一个面向连接的微端口的配 (图28带MCM的面向连接环境) 面向连接的微端口控制着一个或多个网络接口卡(NC)并且在面向连接协议(面向连接 的客户方和呼叫管理器)和NC硬件之间提供了一个接口。 呼叫管理器是一个NDS协议,它对面向连接的客户方提供了呼叫的建立和断开服务 呼叫管理器使用面向连接微端口的发送和接收功能来与网络入口,例如网络交换机实体或远 程的对等层交换信令消息。一个呼叫管理器支持一个或多个信令协议,例如,ATM论坛所 提供的 ATM UNI3.1 MCM驱动程序是一个面向连接的微端口,它也对面向连接的客户方提供了呼叫管理服 务。虽然一个MCM对客户提供的面向连接的服务与一个呼叫管理器伴随一个面向连接的微 端口提供的服务相同,但是呼叫管理器/微端口接口是在驱动程序内部的,因此对于NDIS 是不透明的 多个呼叫管理器和/或MCM可以共存在同一个环境下,并且每个呼叫管理器或 MCM可以支持多个信令协议 个面向连接的客户方使用呼叫管理器或MCM提供的呼叫建立和断开服务。一个面向 连接的客户方同样也使用面向连接的微端口或MCM的发送和接收功能,来发送和接收数 面向连接的客户方与一个无连接协议的共同点在于,对于它的上层应用,它们都提供了 自己的网络和传输层服务,但是,不同于无连接协议的是,面向连接的客户方对于它的下层 它使用了呼叫管理器和面向连接微端口服务或它使用了MCM服务。 个面向连接的客户方可以是一个在早期协议和面向连接NDS之间的一个适配层(它 可以是一个中间层驱动程序)。这种适配层的例子如PATM和LAN仿真器(LANE),它们都 对下层连接使用了呼叫管理器服务,但对上层非连接协议都隐藏了面向连接特性的接口。鉴 于这样的面向连接客户方的上层接口定义已超出了NDS文档的范围,如果一个客户方正作 为一个适配层服务的的话,那么它的上层接口应是由适应面向连接的NDS的上层协议定义 的 关于更多的有关面向连接驱动程序的信息请见第四部分
- 21- 图 2.6 显示了无连接网络程序的 NDIS 环境。 (图 2.6 无连接网络驱动程序环境) 无连接环境是为无连接介质例如 Ethernet 和 Token Ring 所提供的标准的网络驱动程序 环境。参见 2.2 节中对这种环境下的驱动程序的描述。 2.4.2 面向连接环境下的网络驱动程序 NDIS 支持以下几种面向连接的驱动程序: ·面向连接的微端口 ·面向连接的客户方 ·呼叫管理器 ·集成的微端口呼叫管理器(MCM) 图 2.7 显示了一个面向连接的客户方,一个呼叫管理器,和一个面向连接的微端口的配 置。 (图 2.8 带 MCM 的面向连接环境) 面向连接的微端口控制着一个或多个网络接口卡(NIC)并且在面向连接协议(面向连接 的客户方和呼叫管理器)和 NIC 硬件之间提供了一个接口。 呼叫管理器是一个 NDIS 协议,它对面向连接的客户方提供了呼叫的建立和断开服务。 呼叫管理器使用面向连接微端口的发送和接收功能来与网络入口,例如网络交换机实体或远 程的对等层交换信令消息。一个呼叫管理器支持一个或多个信令协议,例如,ATM 论坛所 提供的 ATM UNI3.1。 MCM 驱动程序是一个面向连接的微端口,它也对面向连接的客户方提供了呼叫管理服 务。虽然一个 MCM 对客户提供的面向连接的服务与一个呼叫管理器伴随一个面向连接的微 端口提供的服务相同,但是呼叫管理器/微端口接口是在驱动程序内部的,因此对于 NDIS 是不透明的。 多个呼叫管理器和/或 MCM 可以共存在同一个环境下,并且每个呼叫管理器或 MCM 可以支持多个信令协议。 一个面向连接的客户方使用呼叫管理器或 MCM 提供的呼叫建立和断开服务。一个面向 连接的客户方同样也使用面向连接的微端口或 MCM 的发送和接收功能,来发送和接收数 据。 面向连接的客户方与一个无连接协议的共同点在于,对于它的上层应用,它们都提供了 自己的网络和传输层服务,但是,不同于无连接协议的是,面向连接的客户方对于它的下层, 它使用了呼叫管理器和面向连接微端口服务或它使用了 MCM 服务。 一个面向连接的客户方可以是一个在早期协议和面向连接 NDIS 之间的一个适配层(它 可以是一个中间层驱动程序)。这种适配层的例子如 IP/ATM 和 LAN 仿真器(LANE),它们都 对下层连接使用了呼叫管理器服务,但对上层非连接协议都隐藏了面向连接特性的接口。鉴 于这样的面向连接客户方的上层接口定义已超出了 NDIS 文档的范围,如果一个客户方正作 为一个适配层服务的的话,那么它的上层接口应是由适应面向连接的 NDIS 的上层协议定义 的。 关于更多的有关面向连接驱动程序的信息请见第四部分
243WAN网络驱动程序的环境 Windows2000既支持广域网(WAN)通过无连接介质连接,也支持通过面向连接介 质的连接。图29显示了WAN环境。 下图显示了远程访问服务(RAS)结构 (图29 Windows2000WAN环境) WAN环境包括以下的组件 远程访问服务(RAS RAS允许用户模式的应用程序使用拨号连接,在一个RAS连接建立以后,一个用户的 应用程序可以通过标准的网络接口,例如 Windows Sockets、 Netbios、 Named pipe或RPC 与网络服务相连 TAPI服务提供者 TAP服务提供者是一个用户模式组件,它通过服务提供接口(SPI)发出的呼叫建立和断 开请求,来接受来自RAS客户方和TAP- aware应用的请求。并将TAPI请求传递给 NDIS TAPI (如果呼叫是通过无连接媒体)或 TAPI proxy(如果呼叫是通过面向连接媒体)。TAPI服务 提供者将把SPI请求转换为TAPI请求。 NDISTAPL NDISTAPⅠ是内核模式的组件,它向TAPI设备提供无连接的微端口。 NDISTAPI接受 来自TAP服务提供者的呼叫建立和断开请求,并且通过 NDIS WAN将请求传递给适当的微 端口以建立,监听和断开线路及呼叫 NDPROXY TAPⅠ代理是一个内核模式组件,它向TAPⅠ设备提供面向连接的微端口。 NOPROXY 对于 NDISWAN相当于一个呼叫管理器,对于面向连接的微端口相当于一个面向连接的客 户方。(更多的关于面向连接的网络环境信息请看24.2节) NDIS WAN NDIS WAN是一个中层NDS驱动程序,它完成PPP协议/链接组帧,压缩和加密功能。 NDISWAN将来自上层传输驱动程序的 NDIS PACKET包转换成 NDIS WAN PACKET包 并且将转换后的包传到下层的WAN微端口驱动程序。 NDIS WAN既支持无连接也支持面向 连接的微端口。 当工作在一个面向连接的环境下, NDISWAN对于TAPI代理驱动程序相当于一个面向 连接的客户方,对一个呼叫管理器提供了 NDIS WAN接口 ·WAN微端口 个WAN微端口和非 WAN NDIS微端口调用许多相同的NDS函数并提供许多相同的 句柄。但是当一个WAN微端口发送包和向上层协议指示收到包时,它调用特殊的与WAN 相关的NDS函数。WAN微端口也使用 NDIS WAN PACKET结构而不是 NDIS PACKET 结构。同时,WAN微端口维护WAN相关信息并且对这种特殊的WAN的查询信息给予响 应。一个WAN微端口支持无连接介质或面向连接的介 串行驱动程序 串行驱动程序是一个对内部串行口或多端口串行卡的标准化设备驱动程序。为Wind 200内置的异步WAN微端口使用内部串行驱动程序来进行调制解调通信
- 22- 2.4.3 WAN 网络驱动程序的环境 Windows 2000 既支持广域网(WAN)通过无连接介质连接,也支持通过面向连接介 质的连接。图 2.9 显示了 WAN 环境。 下图显示了远程访问服务(RAS)结构。 (图 2.9 Windows 2000 WAN 环境) WAN 环境包括以下的组件: ·远程访问服务(RAS) RAS 允许用户模式的应用程序使用拨号连接,在一个 RAS 连接建立以后,一个用户的 应用程序可以通过标准的网络接口,例如 Windows Sockets、NetBIOS、Named pipe 或 RPC 与网络服务相连。 ·TAPI 服务提供者 TAPI 服务提供者是一个用户模式组件,它通过服务提供接口(SPI)发出的呼叫建立和断 开请求,来接受来自RAS客户方和TAPI-aware应用的请求。并将TAPI 请求传递给NDIS TAPI (如果呼叫是通过无连接媒体)或 TAPI proxy(如果呼叫是通过面向连接媒体)。TAPI 服务 提供者将把 SPI 请求转换为 TAPI 请求。 ·NDISTAPL NDISTAPI 是内核模式的组件,它向 TAPI 设备提供无连接的微端口。NDISTAPI 接受 来自 TAPI 服务提供者的呼叫建立和断开请求,并且通过 NDISWAN 将请求传递给适当的微 端口以建立,监听和断开线路及呼叫。 ·NDPROXY TAPI 代理是一个内核模式组件,它向 TAPI 设备提供面向连接的微端口。NOPROXY 对于 NDISWAN 相当于一个呼叫管理器,对于面向连接的微端口相当于一个面向连接的客 户方。(更多的关于面向连接的网络环境信息请看 2.4.2 节) · NDISWAN NDISWAN 是一个中层 NDIS 驱动程序,它完成 PPP 协议/链接组帧,压缩和加密功能。 NDISWAN 将来自上层传输驱动程序的 NDIS_PACKET 包转换成 NDIS_WAN_ PACKET 包 并且将转换后的包传到下层的 WAN 微端口驱动程序。NDISWAN 既支持无连接也支持面向 连接的微端口。 当工作在一个面向连接的环境下,NDISWAN 对于 TAPI 代理驱动程序相当于一个面向 连接的客户方,对一个呼叫管理器提供了 NDISWAN 接口。 ·WAN 微端口 一个WAN微端口和非 WAN NDIS 微端口调用许多相同的 NDIS函数并提供许多相同的 句柄。但是当一个 WAN 微端口发送包和向上层协议指示收到包时,它调用特殊的与 WAN 相关的 NDIS 函数。WAN 微端口也使用 NDIS_WAN_PACKET 结构而不是 NDIS_PACKET 结构。同时,WAN 微端口维护 WAN 相关信息并且对这种特殊的 WAN 的查询信息给予响 应。一个 WAN 微端口支持无连接介质或面向连接的介质。 ·串行驱动程序 串行驱动程序是一个对内部串行口或多端口串行卡的标准化设备驱动程序。为 Windows 2000 内置的异步 WAN 微端口使用内部串行驱动程序来进行调制解调通信
第三章网络驱动程序编程要点 这一章将讲述当编写 Windows2000的任何网络驱动程序时通常要考虑的几点问 为 windows2000编写的网络驱动程序应完成许多共同的目标: 跨平台的可移植性 对于多处理器系统的可变性 ·简化软件硬件设置 基于对象的接口 ·支持异步LO 以下是几节为 Windows2000网络驱动程序编写者讲述设计目标的实现 3.1可移植性 3.2多处理器支持 3.3IROLS 34同步和指示 3.5包结构 3.6使用共享内存 3.7异步lO和完成函数 3.1可移植性 NDS驱动程序应很容易在支持 Windows2000的平台间移植。一般说来,从一个硬件 平台移植到另一个平台只需要将它在兼容系统的编译中重新编译即何。 驱动程序开发者应当避免调用与操作系统相关的函数,因为这将使他们的驱动程序不可 移植。应用NDIS函数替换这些调用,只调用NDS函数将使代码在支持NDS的微软操作 系统中可移植,NDS为编写驱动程序提供了很多支持函数,并且直接调用操作系统是不需 要的。 驱动程序应用C来编写。更近一步,驱动程序代码应当限制在 ANSI C标准并且应当避 免使用不被其他兼容系统编译器支持的语言特性。驱动程序代码编写不应当使用任何ANSI C标准指明为“ implementation defined”的特性来编写。 驱动程序应当避免使用任何在不同平台上大小和结构变化的数据类型,驱动程序代码不 应该调用任何C的运行时库函数,而是限于调用NDS提供的函数。 在内核模式下漂移指针指针是不允许的。试图运行这样的操作将是一个致命的错误。 如果驱动程序代码要支持特殊平台的特性,那么这些代码应当包含在# indef和# endif声 明之间。 3.2多处理器支持 编写的代码应当可以在多处理器系统中安全运行。这对于编写可移植的 Windows2000 驱动程序是很重要的。一个网络驱动程序必须使用NDS函数库提供的多处理器安全保证 在单处理器环境下,在一个时刻单处理器只运行一条机器指令,既使这样,当包到达或 时间中断发生时,对于NC或其他设备执行的中断也可能发生。典型的,当正在操纵数据 结构时,例如它的队列时,驱动程序对NC发出停用中断来操纵数据,随后再发生可用中
- 23- 第三章 网络驱动程序编程要点 这一章将讲述当编写 Windows 2000 的任何网络驱动程序时通常要考虑的几点问 题。 为 windows 2000 编写的网络驱动程序应完成许多共同的目标: ·跨平台的可移植性 ·对于多处理器系统的可变性 ·简化软件硬件设置 ·基于对象的接口 ·支持异步 I/O 以下是几节为 Windows 2000 网络驱动程序编写者讲述设计目标的实现: 3.1 可移植性 3.2 多处理器支持 3.3 IRQLs 3.4 同步和指示 3.5 包结构 3.6 使用共享内存 3.7 异步 I/O 和完成函数 3.1 可移植性 NDIS 驱动程序应很容易在支持 Windows 2000 的平台间移植。一般说来,从一个硬件 平台移植到另一个平台只需要将它在兼容系统的编译中重新编译即何。 驱动程序开发者应当避免调用与操作系统相关的函数,因为这将使他们的驱动程序不可 移植。应用 NDIS 函数替换这些调用,只调用 NDIS 函数将使代码在支持 NDIS 的微软操作 系统中可移植,NDIS 为编写驱动程序提供了很多支持函数,并且直接调用操作系统是不需 要的。 驱动程序应用 C 来编写。更近一步,驱动程序代码应当限制在 ANSI C 标准并且应当避 免使用不被其他兼容系统编译器支持的语言特性。驱动程序代码编写不应当使用任何 ANSI C 标准指明为“implementation defined”的特性来编写。 驱动程序应当避免使用任何在不同平台上大小和结构变化的数据类型,驱动程序代码不 应该调用任何 C 的运行时库函数,而是限于调用 NDIS 提供的函数。 在内核模式下漂移指针指针是不允许的。试图运行这样的操作将是一个致命的错误。 如果驱动程序代码要支持特殊平台的特性,那么这些代码应当包含在#ifdef 和#endif 声 明之间。 3.2 多处理器支持 编写的代码应当可以在多处理器系统中安全运行。这对于编写可移植的 Windows 2000 驱动程序是很重要的。一个网络驱动程序必须使用 NDIS 函数库提供的多处理器安全保证。 在单处理器环境下,在一个时刻单处理器只运行一条机器指令,既使这样,当包到达或 时间中断发生时,对于 NIC 或其他设备执行的中断也可能发生。典型的,当正在操纵数据 结构时,例如它的队列时,驱动程序对 NIC 发出停用中断来操纵数据,随后再发生可用中
断。许多线程在单处理器环境下表现的好像是在同一时刻运行的,但是实际上它们却是在独 立的时间片上运行的 在多处理器环境下,处理器同时可运行多条机器指令,一个驱动程序必须异步化,这使 得当一个驱动程序函数正在操纵一个数据结构时,同样的在其他处理器运行的驱动程序函数 不能修改共享的数据。在一个SMP机器中,所有的驱动程序代码都要重新装入,为了消除 这种资源保护问题, Windows2000驱动程序使用自旋锁。对于NDIS微端口,NDIS函数库 可处理许多这些多处理器问题。NDS库对请求进行排队和串行化调用标准的微端口函数, 除非有以下情况之 在设计和实现中,NC为无连接网络介质设计,并且微端口设计为非串行化的,这或 许是因为NC有支持包排队和同步化的部分,或者因为驱动程序的编写者喜欢来管理排队 和在微端口内实现同步问题 NIC为面向连接介质设计,NDS认为任何面向连接的NIC驱动程序是一个非串行化 的微端口 但是大多数无连接的NC驱动程序都是串行化的微端口,因为他们依赖NDS来管理 包排队,同步化和串行化问题。 3.3 IRQL 所有NDS调用的驱动程序函数都运行在系统决定的IRQL下 PASSIVE LEVEL< DLSPATCH LEVEL< DIRQL中的一个。例如,一个微端口初始化函数、 挂起函数、重启函数和有时的关闭函数通常都运行在 PASSIVE LEVEL下。中断代码运行 在 DIRQL下,所以NDS中层或协议驱动程序从不运行在DRQL下。所有其他的NDS驱 动程序函数运行在IRQL<= DISPATCH LEVEL下 驱动程序函数运行于的IRQL将影响调用什么样的NDS函数。特定的函数只可在IRQL PASSIVE LEVEL下调用,其他的函数可在 DISPATCH LEⅤEL或更低层调用。一个驱动程 序的编写者应当检查每一个NDS函数的TRQL限制。 任何与驱动程序的ISR共享资源的驱动程序函数必须能将它的IRQL升级到 DTRQL来 防止争用情况的发生,NDS提供了这种机质。 3.4同步和指示 当两个线程共享可被同时访问的资源时,无论是单处理机还是SMP,同步是必须的 例如,对于一个单处理机,如果一个驱动程序正在访问一个共享资源时,被一个运行在更高 IRQL(例如ISR)的函数中断时,必须保护共享资源以阻止这种争用的发生而使资源处于 不确定状态。在一个SMP中,两个线程可以在同一时刻运行,在不同处理器上并且试图来 修改同一数据的访问必须同步 NDS提供了自旋锁可以用来对在同一IRQL下运行的线程间访问共享资源实现同步 当两个线程在不同IRQL下访问共享资源时,NDS提供了一种机制来临时升高低IRQL代 码的IRQL,以使得访问共享资源串行化。 当一个线程依赖于一个外部事件的发生时,指示是必须的。例如,对于驱动程序,当经 过一定周期后,必须指示它以使它可以检查它的驱动设备。或者一个NC设备驱动程序要 进行周期性的操作例如轮询,时钟提供了这种机质。 事件提供了一种两个执行的线程可进行同步操作的机质。例如,一个微端口驱动程序通 过向设备写数据来测试NC上的中断,它必须待等一种中断来指示驱动程序操作是成功的
- 24- 断。许多线程在单处理器环境下表现的好像是在同一时刻运行的,但是实际上它们却是在独 立的时间片上运行的。 在多处理器环境下,处理器同时可运行多条机器指令,一个驱动程序必须异步化,这使 得当一个驱动程序函数正在操纵一个数据结构时,同样的在其他处理器运行的驱动程序函数 不能修改共享的数据。在一个 SMP 机器中,所有的驱动程序代码都要重新装入,为了消除 这种资源保护问题,Windows 2000 驱动程序使用自旋锁。对于 NDIS 微端口,NDIS 函数库 可处理许多这些多处理器问题。NDIS 库对请求进行排队和串行化调用标准的微端口函数, 除非有以下情况之一: ·在设计和实现中,NIC 为无连接网络介质设计,并且微端口设计为非串行化的,这或 许是因为 NIC 有支持包排队和同步化的部分,或者因为驱动程序的编写者喜欢来管理排队 和在微端口内实现同步问题。 ·NIC 为面向连接介质设计,NDIS 认为任何面向连接的 NIC 驱动程序是一个非串行化 的微端口。 但是大多数无连接的 NIC 驱动程序都是串行化的微端口,因为他们依赖 NDIS 来管理 包排队,同步化和串行化问题。 3.3 IRQL 所 有 NDIS 调 用 的 驱 动 程 序 函 数 都 运 行 在 系 统 决 定 的 IRQL 下 , PASSIVE_LEVEL<DLSPATCH_LEVEL<DIRQL 中的一个。例如,一个微端口初始化函数、 挂起函数、重启函数和有时的关闭函数通常都运行在 PASSIVE_LEVEL 下。中断代码运行 在 DIRQL 下,所以 NDIS 中层或协议驱动程序从不运行在 DIRQL 下。所有其他的 NDIS 驱 动程序函数运行在 IRQL<=DISPATCH_LEVEL 下。 驱动程序函数运行于的 IRQL 将影响调用什么样的 NDIS 函数。特定的函数只可在 IRQL PASSIVE_LEVEL 下调用,其他的函数可在 DISPATCH_LEVEL 或更低层调用。一个驱动程 序的编写者应当检查每一个 NDIS 函数的 TRQL 限制。 任何与驱动程序的 ISR 共享资源的驱动程序函数必须能将它的 IRQL 升级到 DTRQL 来 防止争用情况的发生,NDIS 提供了这种机质。 3.4 同步和指示 当两个线程共享可被同时访问的资源时,无论是单处理机还是 SMP,同步是必须的。 例如,对于一个单处理机,如果一个驱动程序正在访问一个共享资源时,被一个运行在更高 IRQL(例如 ISR)的函数中断时,必须保护共享资源以阻止这种争用的发生而使资源处于 不确定状态。在一个 SMP 中,两个线程可以在同一时刻运行,在不同处理器上并且试图来 修改同一数据的访问必须同步。 NDIS 提供了自旋锁可以用来对在同一 IRQL 下运行的线程间访问共享资源实现同步。 当两个线程在不同 IRQL 下访问共享资源时,NDIS 提供了一种机制来临时升高低 IRQL 代 码的 IRQL,以使得访问共享资源串行化。 当一个线程依赖于一个外部事件的发生时,指示是必须的。例如,对于驱动程序,当经 过一定周期后,必须指示它以使它可以检查它的驱动设备。或者一个 NIC 设备驱动程序要 进行周期性的操作例如轮询,时钟提供了这种机质。 事件提供了一种两个执行的线程可进行同步操作的机质。例如,一个微端口驱动程序通 过向设备写数据来测试 NIC 上的中断,它必须待等一种中断来指示驱动程序操作是成功的
事件可以在等待中断完成的线程与处理中断的线程之间进行同步操作。 以下是这些NDS机制的描述。 自旋锁 自旋锁提供了一个用来保护共享资源的同步机制,这种资源是单处理器或一个多处理机 下的、运行在IRQL> PASSIVE LEⅤEL下的、内核模式中的线程所共享使用的。一个自旋锁 在同时运行在一个SMP机上不同的执行线程之间提供同步。一个线程在访问保护资源前获 得一个自旋锁。自旋锁使得任务线程中只有持有自旋锁的线程可使用资源。一个等待自旋锁 的线程将在试图获得锁时间内循环,直到持有锁的线程释放为止。 自旋锁的另一个特点是与IRQL有关。试图获得自旋锁将请求线程的IRQL临时升高为 正在执行线程的IRQL。这阻止了运行在同一处理器上的低IRQL线程优先获得执行线程 运行在高IRαL的线程可以优先获得执行线程,但这些线程不能获得自旋锁,因为自旋锁比 它们的IRαL低。因此,其他运行在同一处理器上的线程只有到自旋锁被执行线程获得并释 放后才可试图获得自旋锁。一个编写很好的网络驱动程序应该会减少自旋锁持有的时间。 个典型的使用自旋锁的例子是保护一个队列。例如,微端口发送函数 MiniportSend 将协议驱动程序传来的包进行排队。因为其他驱动程序函数也使用这个队列, MiniportSend 必须用一个自旋锁保护这个队列使得在一个时刻只有一个线程可操纵这个队列。 Miniport send获得自旋锁,添加包到队列后释放自旋锁。使用自旋锁保证持锁线程是唯一修改队列 的线程,同时使得包被安全地添加到队列中。当NC驱动程序从队列中取走包时,通过同 样的自旋锁保护这个访问。当执行指令修改队列头或任何队列组成域时,驱动程序必须用自 旋锁保护队列。 一个驱动程序不能过于保护一个队列。例如,一个驱动程序可以在网络驱动程序将包进 行排队之前在包的预留字段进行操作(例如,填充包含长度的字段)。驱动程序可以在自旋锁 保护区域之外进行这种操作,但必须是在进行包排队之前。一旦包排入队列并且执行线程释 放了自旋锁,驱动程序应认为其他线程可以立即将包从队列中取出 避免死锁问题 Windows2000并不限制网络驱动程序同时持有多于一个的自旋锁。但是,驱动程序的 某部分在持有自旋锁B时,试图获得自旋锁A,并且其他部分在持有锁A时,试图获得自 旋锁B时,死锁就会发生。如果要获得多于一个的自旋锁,驱动程序应当通过强制以某 顺序获得锁来避免死锁,这就是说,如果一个驱动程序强制在获得自旋锁A之后才可获得 锁B,那么上述情况就不会发生 总得来说,使用自旋锁将对系统性能带来负面效应,所以驱动程序不应当使用许多锁。 有时,完全不同的函数(例如,发送和接收函数)有不太要紧的重叠可使用两个自旋锁。使用 多于一个自旋锁是为了允许两个运行在独立处理器上的函数独立操作的折中方案。 时钟 时钟被用来轮询或进行超时操作的。一个驱动程序可以产生一个时钟并联合一个函数时 钟上。当一个特定周期时钟期满时,调用相关函数。时钟可以是一次的或周期性的,一但设 置了一个周期时钟,当每个周期结束时都会触发,直到它被完全清除掉为止。一次性时钟在 触发后必须重新设置。 时钟通过调用 NdisMInitialize Timer来产生和初始化,并且通过调用 NdisMsetTimer来 设置,也可调用 NdisMset Periodictimer设置周期时钟。如果使用了一个非周期时钟,那么 通过调用 NdisMSet PeriodicTimer重新设置时钟。通过调用 NdisMCancelTimer可以清除时钟。 事件 事件在两个执行线程之间实现同步操作。一个事件通过一个驱动程序装入并且通过调用 NdisInitialize Event初始化。一个运行在 IRQL PASSIⅤELEⅤEL下的线程调用 NdisWait Event
- 25- 事件可以在等待中断完成的线程与处理中断的线程之间进行同步操作。 以下是这些 NDIS 机制的描述。 自旋锁 自旋锁提供了一个用来保护共享资源的同步机制,这种资源是单处理器或一个多处理机 下的、运行在 IRQL>PASSIVE_LEVEL 下的、内核模式中的线程所共享使用的。一个自旋锁 在同时运行在一个 SMP 机上不同的执行线程之间提供同步。一个线程在访问保护资源前获 得一个自旋锁。自旋锁使得任务线程中只有持有自旋锁的线程可使用资源。一个等待自旋锁 的线程将在试图获得锁时间内循环,直到持有锁的线程释放为止。 自旋锁的另一个特点是与 IRQL 有关。试图获得自旋锁将请求线程的 IRQL 临时升高为 正在执行线程的 IRQL。这阻止了运行在同一处理器上的低 IRQL 线程优先获得执行线程, 运行在高 IRQL 的线程可以优先获得执行线程,但这些线程不能获得自旋锁,因为自旋锁比 它们的 IRQL 低。因此,其他运行在同一处理器上的线程只有到自旋锁被执行线程获得并释 放后才可试图获得自旋锁。一个编写很好的网络驱动程序应该会减少自旋锁持有的时间。 一个典型的使用自旋锁的例子是保护一个队列。例如,微端口发送函数 MiniportSend 将协议驱动程序传来的包进行排队。因为其他驱动程序函数也使用这个队列,MiniportSend 必须用一个自旋锁保护这个队列使得在一个时刻只有一个线程可操纵这个队列。Miniport Send 获得自旋锁,添加包到队列后释放自旋锁。使用自旋锁保证持锁线程是唯一修改队列 的线程,同时使得包被安全地添加到队列中。当 NIC 驱动程序从队列中取走包时,通过同 样的自旋锁保护这个访问。当执行指令修改队列头或任何队列组成域时,驱动程序必须用自 旋锁保护队列。 一个驱动程序不能过于保护一个队列。例如,一个驱动程序可以在网络驱动程序将包进 行排队之前在包的预留字段进行操作(例如,填充包含长度的字段)。驱动程序可以在自旋锁 保护区域之外进行这种操作,但必须是在进行包排队之前。一旦包排入队列并且执行线程释 放了自旋锁,驱动程序应认为其他线程可以立即将包从队列中取出。 避免死锁问题 Windows 2000 并不限制网络驱动程序同时持有多于一个的自旋锁。但是,驱动程序的 某部分在持有自旋锁 B 时,试图获得自旋锁 A,并且其他部分在持有锁 A 时,试图获得自 旋锁 B 时,死锁就会发生。如果要获得多于一个的自旋锁,驱动程序应当通过强制以某一 顺序获得锁来避免死锁,这就是说,如果一个驱动程序强制在获得自旋锁 A 之后才可获得 锁 B,那么上述情况就不会发生。 总得来说,使用自旋锁将对系统性能带来负面效应,所以驱动程序不应当使用许多锁。 有时,完全不同的函数(例如,发送和接收函数)有不太要紧的重叠可使用两个自旋锁。使用 多于一个自旋锁是为了允许两个运行在独立处理器上的函数独立操作的折中方案。 时钟 时钟被用来轮询或进行超时操作的。一个驱动程序可以产生一个时钟并联合一个函数时 钟上。当一个特定周期时钟期满时,调用相关函数。时钟可以是一次的或周期性的,一但设 置了一个周期时钟,当每个周期结束时都会触发,直到它被完全清除掉为止。一次性时钟在 触发后必须重新设置。 时钟通过调用 NdisMInitializeTimer 来产生和初始化,并且通过调用 NdisMsetTimer 来 设置,也可调用 NdisMsetPeriodicTimer 设置周期时钟。如果使用了一个非周期时钟,那么 通过调用 NdisMSetPeriodicTimer 重新设置时钟。通过调用 NdisMCancelTimer 可以清除时钟。 事件 事件在两个执行线程之间实现同步操作。一个事件通过一个驱动程序装入并且通过调用 NdisInitializeEvent 初始化。一个运行在 IRQL PASSIVE_LEVEL 下的线程调用 NdisWaitEvent