1.3.1移植性 Windows2000输出两个系统提供的运行时库,它们包含了以Rtl为前缀的函数。内核模式 驱动程序可以使用相同的内核模式Rt1Xx例程作为执行组件,但是不能调用用户模式 TxxX例程。 每个NT执行组件输出一组内核模式支持的例程,驱动程序和所有其他的内核模式组件可以 调用这些例程。如果支持例程的底层实现随着时间的推移发生了变化,它的调用者依然可 以保持不变,因为定义组件的接口没有发生变化。 大多数 Windows2000组件完全是用C写成的,仅仅少部分的HAL和内核是用汇编语言写成 的。所以操作系统对于硬件平台来说是容易移植的。内核模式驱动程序应该也使用C来 写,从而使得它能被系统兼容的C编译器重新编译、链接,并且运行在不同的 Windows 2000平台上。如果是一个wDM驱动程序,不用重新书写驱动程序部分的代码,也不用替换 驱动程序中的模块,就可以在 Windows98平台上运行 如果相同的特性不能保证被其他系统兼容的编译器所支持,驱动程序则不应该依赖于任何 特定系统兼容的C编译器或C支持库的特性。通常,驱动程序代码应该符合 ANSI C标准 要避免使用任何这个标准中描述为“实现定义的”的部分 特别地,编写可移植的 Windows2000驱动程序,最好避免以下的情况: 对数据类型的依赖,因为它可能随着平台的不同而在大小和表现形式上有所不同 调用任何保持状态的标准C运行时库函数 调用任何标准C运行时库函数,而操作系统已经提供了一个可以替换被调用函数的支 持例程 K提供了一组包含文件,这些文件中定义了系统特定的数据类型和常数,驱动程序(以 及所有其他内核模式组件)可以使用它们来保证平台之间的可移植性。大多数驱动程序包 含了主DDK内核模式的包含文件之一,这些文件是: ntddk.h和wdm.h。这些主包含文件不 仅收录了系统提供的头文件,它们定义了基本的内核模式类型;而且,还收录了针对不同 处理器构架特定的头文件的合适选择,从而驱动程序能被使用相应的编译器指令编译。 对于WDM驱动程序,DK提供了一组包含文件,它们输出 Windows2000内核模式支持例 程、宏、常数以及类型的一个子集。WDM驱动程序使用主DDK包含文件wdm.h而不是 ntddk.h。包含wdm.h以及只使用它所输出的支持例程、宏、常数以及类型,使得WDM驱动 程序可以象在 Windows2000上一样在 Windows98上编译运行 如果驱动程序需要与平台有关的定义,最好是将这些定义用# ifdef语句单独分割开,这样 就能使驱动程序针对合适的硬件平台被编译和连接。当然,使用DK主头文件提供的支持 例程、宏、常数和类型,你几乎可以避免实现任何与平台有关的条件编译代码 某些驱动程序包含系统提供的其他头文件来确保 Windows平台上的可移植性,特别是 SCSI、NIS、以及视频微端口驱动程序。关于这些类型的驱动程序的头文件的详细信息分 别参看第3部分,“存储驱动程序”;以及在线DDK中的“ Graphics Driver Desi Guide(图形驱动程序设计指南)”和“ Reference(参考)”,“ Network Driver guide (网络驱动程序设计指南)”和“ Reference(参考)” 1.3.2可配置性 因为 Windows nt/ Windows2000是一个可移植的操作系统,所以,设备及其驱动程序必须 是硬件可配置和软件可配置的
6 1.3.1 移植性 Windows 2000 输出两个系统提供的运行时库,它们包含了以 Rtl 为前缀的函数。内核模式 驱动程序可以使用相同的内核模式 RtlXxx 例程作为执行组件,但是不能调用用户模式 RtlXxx 例程。 每个 NT 执行组件输出一组内核模式支持的例程,驱动程序和所有其他的内核模式组件可以 调用这些例程。如果支持例程的底层实现随着时间的推移发生了变化,它的调用者依然可 以保持不变,因为定义组件的接口没有发生变化。 大多数 Windows 2000 组件完全是用 C 写成的,仅仅少部分的 HAL 和内核是用汇编语言写成 的。所以操作系统对于硬件平台来说是容易移植的。内核模式驱动程序应该也使用 C 来 写,从而使得它能被系统兼容的 C 编译器重新编译、链接,并且运行在不同的 Windows 2000 平台上。如果是一个 WDM 驱动程序,不用重新书写驱动程序部分的代码,也不用替换 驱动程序中的模块,就可以在 Windows 98 平台上运行。 如果相同的特性不能保证被其他系统兼容的编译器所支持,驱动程序则不应该依赖于任何 特定系统兼容的 C 编译器或 C 支持库的特性。通常,驱动程序代码应该符合 ANSI C 标准, 要避免使用任何这个标准中描述为“实现定义的”的部分。 特别地,编写可移植的 Windows 2000 驱动程序,最好避免以下的情况: ▪ 对数据类型的依赖,因为它可能随着平台的不同而在大小和表现形式上有所不同 ▪ 调用任何保持状态的标准 C 运行时库函数 ▪ 调用任何标准 C 运行时库函数,而操作系统已经提供了一个可以替换被调用函数的支 持例程 DDK 提供了一组包含文件,这些文件中定义了系统特定的数据类型和常数,驱动程序(以 及所有其他内核模式组件)可以使用它们来保证平台之间的可移植性。大多数驱动程序包 含了主 DDK 内核模式的包含文件之一,这些文件是:ntddk.h 和 wdm.h。这些主包含文件不 仅收录了系统提供的头文件,它们定义了基本的内核模式类型;而且,还收录了针对不同 处理器构架特定的头文件的合适选择,从而驱动程序能被使用相应的编译器指令编译。 对于 WDM 驱动程序,DDK 提供了一组包含文件,它们输出 Windows 2000 内核模式支持例 程、宏、常数以及类型的一个子集。WDM 驱动程序使用主 DDK 包含文件 wdm.h 而不是 ntddk.h。包含 wdm.h 以及只使用它所输出的支持例程、宏、常数以及类型,使得 WDM 驱动 程序可以象在 Windows 2000 上一样在 Windows 98 上编译运行。 如果驱动程序需要与平台有关的定义,最好是将这些定义用#ifdef 语句单独分割开,这样 就能使驱动程序针对合适的硬件平台被编译和连接。当然,使用 DDK 主头文件提供的支持 例程、宏、常数和类型,你几乎可以避免实现任何与平台有关的条件编译代码。 某些驱动程序包含系统提供的其他头文件来确保 Windows 平台上的可移植性,特别是, SCSI、NDIS、以及视频微端口驱动程序。关于这些类型的驱动程序的头文件的详细信息分 别参看第 3 部分,“存储驱动程序”;以及在线 DDK 中的“Graphics Driver Design Guide(图形驱动程序设计指南)”和“Reference(参考)”, “Network Driver Guide (网络驱动程序设计指南)”和“Reference(参考)”。 1.3.2 可配置性 因为 Windows NT/Windows 2000 是一个可移植的操作系统,所以,设备及其驱动程序必须 是硬件可配置和软件可配置的
例如,在一个热插拔的RAID(廉价磁盘冗余阵列)配置中,用户可以在运行的时候替换磁 盘。这样的设备被称为是硬件可配置的。它的驱动程序不能假定给定的磁盘设备会在某个 特定的位置固定。因此,为了保持可移植性,它的驱动程序一定不能包含硬编码的与机器 有关的值 这类磁盘设备的驱动程序可以在任何给定的机器上对一个或多个文件系统提供硬件级支 持,当然这取决于用户如何对这些磁盘分区。另外,中间层驱动程序能为高层文件系统驱 动程序提供灾难恢复(镜像分区、带集、和/或卷集)支持,这样的机器通常具有充足的存 储能力。换句话说,同样的物理磁盘驱动程序能为一些机器提供对多种高层驱动程序的支 持,而对其他的机器则不能。这样的磁盘驱动程序是软件可配置的,它们都是中间层和文 件系统驱动程序,并且它们最终依赖于底层设备和/或即插即用(PnP)硬件总线驱动程 序 Windows nt/2000HAL组件被实现成一个动态连接库,它负责所有的硬件层、平台特定支 持,包括内核模式驱动程序在内,系统中的其他任何组件都需要它的支持。HAL输出的例 程提供了平台硬件与包括所有驱动程序在内的系统软件组件之间的一个接口,这些例程隐 藏了平台特定的硬件细节,例如高速缓存、I/0总线、中断等等。 系统支持的PnP硬件总线驱动程序,在PnP管理器的合作下对HAL组件提供了类似的功 能。这就是说,PnP管理器与每一个PnP硬件总线驱动程序合作,提供特定类型I/0总线 的平台硬件和系统软件之间的接口 PnP管理器创建一个设备树,它的节点代表系统中所有的设备,包括总线和连在总线上的 设备。对于每一个设备,PnP管理器维护两个列表:一个是设备可以使用的硬件资源表, 另一个是设备实际占用的硬件资源表。设备驱动程序辅助PnP管理器创建这些列表,这些 列表被保存在注册表中。一旦在系统中增添或删除设备,PnP管理器有必要重新分配资 ,并更新列表 1.3.3永远抢占优先和永远中断 内核组件确定什么时候依照下列优先级调度标准之一来运行一段特定的代码: 针对线程的内核定义的运行时优先级 系统中每一个线程都被分配了一个优先级属性。通常,系统中的大多数线程拥有可变 的优先级属性:它们是总是抢占优先的,并且当前在同一优先级上的线程被轮询调 度。系统中的一些线程拥有实时优先属性:这些对时间要求苛刻的线程可以被拥有更 高实时优先级的线程抢占,直到它们释放控制 无论拥有什么优先级属性,系统中的任何线程在硬件和某种软件中断发生时被抢占 内核定义的中断请求级(IRL),在给定平台上被赋予特定的中断向量 内核也区分硬件和软件中断的优先次序,使得一些内核模式代码可以在更高的IRL上 运行。从而使得这些代码,包括大多数驱动程序,比系统中所有的线程拥有更高的优 先级。 一段内核模式代码在特定的IRQL上执行,这个IRQL定义了它的硬件优先级。内核模 式代码是总是可中断的:一个带有更高IRL值的中断可能在任何时候发生,从而导致 拥有更高IRL的另一段内核模式代码被处理器立即执行。换句话说,当一段代码在给 定的IRQL上运行时,内核屏蔽微处理器上所有小于等于当前IRQL值的中断向量。 通常,线程运行在 PASSIVW LEVEL IRQL上:没有中断向量被屏蔽。软件中断被赋予相对较 低的IRQL值( APC LEVEL、 DISPATCH LEVEL、或针对内核调试 WAKE LEVEL)。设备中断拥
7 例如,在一个热插拔的 RAID(廉价磁盘冗余阵列)配置中,用户可以在运行的时候替换磁 盘。这样的设备被称为是硬件可配置的。它的驱动程序不能假定给定的磁盘设备会在某个 特定的位置固定。因此,为了保持可移植性,它的驱动程序一定不能包含硬编码的与机器 有关的值。 这类磁盘设备的驱动程序可以在任何给定的机器上对一个或多个文件系统提供硬件级支 持,当然这取决于用户如何对这些磁盘分区。另外,中间层驱动程序能为高层文件系统驱 动程序提供灾难恢复(镜像分区、带集、和/或卷集)支持,这样的机器通常具有充足的存 储能力。换句话说,同样的物理磁盘驱动程序能为一些机器提供对多种高层驱动程序的支 持,而对其他的机器则不能。这样的磁盘驱动程序是软件可配置的,它们都是中间层和文 件系统驱动程序,并且它们最终依赖于底层设备和/或即插即用(PnP)硬件总线驱动程 序。 Windows NT/2000 HAL 组件被实现成一个动态连接库,它负责所有的硬件层、平台特定支 持,包括内核模式驱动程序在内,系统中的其他任何组件都需要它的支持。HAL 输出的例 程提供了平台硬件与包括所有驱动程序在内的系统软件组件之间的一个接口,这些例程隐 藏了平台特定的硬件细节,例如高速缓存、I/O 总线、中断等等。 系统支持的 PnP 硬件总线驱动程序,在 PnP 管理器的合作下对 HAL 组件提供了类似的功 能。这就是说,PnP 管理器与每一个 PnP 硬件总线驱动程序合作,提供特定类型 I/O 总线 的平台硬件和系统软件之间的接口。 PnP 管理器创建一个设备树,它的节点代表系统中所有的设备,包括总线和连在总线上的 设备。对于每一个设备,PnP 管理器维护两个列表:一个是设备可以使用的硬件资源表, 另一个是设备实际占用的硬件资源表。设备驱动程序辅助 PnP 管理器创建这些列表,这些 列表被保存在注册表中。一旦在系统中增添或删除设备,PnP 管理器有必要重新分配资 源,并更新列表。 1.3.3 永远抢占优先和永远中断 内核组件确定什么时候依照下列优先级调度标准之一来运行一段特定的代码: ▪ 针对线程的内核定义的运行时优先级 系统中每一个线程都被分配了一个优先级属性。通常,系统中的大多数线程拥有可变 的优先级属性:它们是总是抢占优先的,并且当前在同一优先级上的线程被轮询调 度。系统中的一些线程拥有实时优先属性:这些对时间要求苛刻的线程可以被拥有更 高实时优先级的线程抢占,直到它们释放控制。 无论拥有什么优先级属性,系统中的任何线程在硬件和某种软件中断发生时被抢占。 ▪ 内核定义的中断请求级(IRQL),在给定平台上被赋予特定的中断向量 内核也区分硬件和软件中断的优先次序,使得一些内核模式代码可以在更高的 IRQL 上 运行。从而使得这些代码,包括大多数驱动程序,比系统中所有的线程拥有更高的优 先级。 一段内核模式代码在特定的 IRQL 上执行,这个 IRQL 定义了它的硬件优先级。内核模 式代码是总是可中断的:一个带有更高 IRQL 值的中断可能在任何时候发生,从而导致 拥有更高 IRQL 的另一段内核模式代码被处理器立即执行。换句话说,当一段代码在给 定的 IRQL 上运行时,内核屏蔽微处理器上所有小于等于当前 IRQL 值的中断向量。 通常,线程运行在 PASSIVW_LEVEL IRQL 上:没有中断向量被屏蔽。软件中断被赋予相对较 低的 IRQL 值(APC_LEVEL、DISPATCH_LEVEL、或针对内核调试 WAKE_LEVEL)。设备中断拥
有较高的IRL值,内核则为系统关键的中断保留最高的IRQL值,例如系统时钟或总线错 误中断 一些系统支持例程运行在 IRQL PASSIVE LEVEL,原因有两个方面:一是因为一些内核模式 组件创建它们自己的线程,另一是因为一些支持例程被作为可分页代码实现,并且/或者访 问可分页数据。 同样的,某种标准驱动程序例程通常运行于 IRQL PASSIVE LEVEL。然而,几个标准的驱动 程序例程要么运行于 IRQL DISPATCH LEVEL,要么运行于设备IRQL(也称为 DIRQL),后 种是针对最低层的驱动程序来说的。参看《内核模式驱动程序设计指南》的第16章中的 “管理硬件优先级”,可以得到关于IRQL的细节。 通常,如果线程正在请求驱动程序的当前I/0操作,则在这些线程的环境中,仅仅最高层 的驱动程序被调用。如果线程已经请求了它的当前I/O操作,中间层或最低层驱动程序从 不假定它正在这些线程的环境中执行。 由于性能的原因(避免环境交换),几乎没有驱动程序创建它们自己的线程。因此,当 个标准的驱动程序例程被调用做某些工作时,驱动程序例程通常恰恰在当前线程环境中执 行。也就是说,它们在专用线程环境中执行。 任何内核模式例程都运行在比 PASSIVE LEVEL高的IRQL上,这些例程拥有比系统中所有线 程都高的优先级。驱动程序中的任何例程都是可中断的:运行在特定IRQL上的任何内核模 式例程一直保留处理器的控制权,除非例程运行时有更高的内核指定的IRQL发生。 甚至一个最底层的驱动程序中断服务例程(ISR)也能被其他运行在较高IRQL上的例程中 断(例如,被另一个驱动程序的ISR中断)。不像一些旧的PC操作系统中的驱动程序 Windows nt/ Windows2000驱动程序的ISR几乎不做任何驱动程序的I/0处理,因为驱动 程序的ISR一直到它返回,也不必保留它正在其上运行的CPU的控制。 相反,最低层的驱动程序必须在较低的IRQL上完成它的I/0操作的大部分,而不是它的 ISR的DIRL。为了获得好的系统整体性能,所有的运行在高级IRQL上的内核模式例程必 须迅速释放CPU的控制权。这样的例程不仅仅做它们在高级IRQL上必须作的,通常将一个 延迟过程调用(DPC)排队,以完成任何能在较低IRQL( DISPATCH LEVEL)上完成的操 作 操作系统的可中断、可抢占优先的设计目标是最大化平均性能。任何驱动程序的ISR能被 运行在较高IR哑L上的例程中断,并且任何线程都能被具有较高优先级的线程中断。虽然一 些线程拥有实时优先级属性,它们仍然能被具有更高优先级的线程抢占。然而 Windows NT/ Windows2000构架并没有提供一个真正的实时系统 如果想得到内核模式驱动程序中系统定义的标准例程的介绍,可以参看第2章,“分层的 I/0、IRP和1/O对象”。参看第4章,“基本驱动程序结构”可以得到这些例程的概述 1.34多处理器安全 在任何 Windows nt/ Windows2000多处理器平台中,需要满足下列条件 所有的CPU是相同的,并且它们要么都有协处理器,要么都没有。 所有的CPU共享内存,并且一致地访问内存。 在对称平台中,每一个CPU能访问内存、处理中断、访问I/0控制寄存器。(相对来 说,在不对称多处理器机器中,一个CPU可能为一组从属CPU处理所有中断。) Windows nt/ Windows2000被设计成一律运行在单一或对称多处理器平台上,内核模式的 Windows2000和WDM驱动程序应该被同样的设计
8 有较高的 IRQL 值,内核则为系统关键的中断保留最高的 IRQL 值,例如系统时钟或总线错 误中断。 一些系统支持例程运行在 IRQL_PASSIVE_LEVEL,原因有两个方面:一是因为一些内核模式 组件创建它们自己的线程,另一是因为一些支持例程被作为可分页代码实现,并且/或者访 问可分页数据。 同样的,某种标准驱动程序例程通常运行于 IRQL_PASSIVE_LEVEL。然而,几个标准的驱动 程序例程要么运行于 IRQL DISPATCH_LEVEL,要么运行于设备 IRQL(也称为 DIRQL),后 一种是针对最低层的驱动程序来说的。参看《内核模式驱动程序设计指南》的第 16 章中的 “管理硬件优先级”,可以得到关于 IRQL 的细节。 通常,如果线程正在请求驱动程序的当前 I/O 操作,则在这些线程的环境中,仅仅最高层 的驱动程序被调用。如果线程已经请求了它的当前 I/O 操作,中间层或最低层驱动程序从 不假定它正在这些线程的环境中执行。 由于性能的原因(避免环境交换),几乎没有驱动程序创建它们自己的线程。因此,当一 个标准的驱动程序例程被调用做某些工作时,驱动程序例程通常恰恰在当前线程环境中执 行。也就是说,它们在专用线程环境中执行。 任何内核模式例程都运行在比 PASSIVE_LEVEL 高的 IRQL 上,这些例程拥有比系统中所有线 程都高的优先级。驱动程序中的任何例程都是可中断的:运行在特定 IRQL 上的任何内核模 式例程一直保留处理器的控制权,除非例程运行时有更高的内核指定的 IRQL 发生。 甚至一个最底层的驱动程序中断服务例程(ISR)也能被其他运行在较高 IRQL 上的例程中 断(例如,被另一个驱动程序的 ISR 中断)。不像一些旧的 PC 操作系统中的驱动程序, Windows NT/Windows 2000 驱动程序的 ISR 几乎不做任何驱动程序的 I/O 处理,因为驱动 程序的 ISR 一直到它返回,也不必保留它正在其上运行的 CPU 的控制。 相反,最低层的驱动程序必须在较低的 IRQL 上完成它的 I/O 操作的大部分,而不是它的 ISR 的 DIRQL。为了获得好的系统整体性能,所有的运行在高级 IRQL 上的内核模式例程必 须迅速释放 CPU 的控制权。这样的例程不仅仅做它们在高级 IRQL 上必须作的,通常将一个 延迟过程调用(DPC)排队,以完成任何能在较低 IRQL(DISPATCH_LEVEL)上完成的操 作。 操作系统的可中断、可抢占优先的设计目标是最大化平均性能。任何驱动程序的 ISR 能被 运行在较高 IRQL 上的例程中断,并且任何线程都能被具有较高优先级的线程中断。虽然一 些线程拥有实时优先级属性,它们仍然能被具有更高优先级的线程抢占。然而 Windows NT/Windows 2000 构架并没有提供一个真正的实时系统。 如果想得到内核模式驱动程序中系统定义的标准例程的介绍,可以参看第 2 章,“分层的 I/O、IRP 和 I/O 对象”。参看第 4 章,“基本驱动程序结构”可以得到这些例程的概述。 1.3.4 多处理器安全 在任何 Windows NT/Windows 2000 多处理器平台中,需要满足下列条件: ▪ 所有的 CPU 是相同的,并且它们要么都有协处理器,要么都没有。 ▪ 所有的 CPU 共享内存,并且一致地访问内存。 ▪ 在对称平台中,每一个 CPU 能访问内存、处理中断、访问 I/O 控制寄存器。(相对来 说,在不对称多处理器机器中,一个 CPU 可能为一组从属 CPU 处理所有中断。) Windows NT/Windows 2000 被设计成一律运行在单一或对称多处理器平台上,内核模式的 Windows 2000 和 WDM 驱动程序应该被同样的设计
为了能在对称多处理器(SMP)平台上安全运行,任何操作系统都必须解决这样一个问题 即,怎样保证正在某个处理器上运行的代码,不会同时访问和修改运行在另一个处理器上 的代码正在访问和修改的数据。例如,正在一个处理器上处理设备中断的最低层驱动程序 的ISR排斥其他对临界的、驱动程序定义的数据(或设备寄存器)的访问,以防它的设备 同时在另一个处理器上中断 此外,在单一处理器的机器中被序列化的驱动程序I/0操作在SMP机器中能被同时执行。 这就是说,当一个处理输入I/0操作的驱动程序例程在一个处理器上被执行的时候,另 个与设备通讯的例程能同时在另一个处理器上被执行。不论是运行在单处理器机器上,还 是SP机器上,都要求包括wDM驱动程序在内的所有内核模式驱动程序能同步访问系统定 义的数据或系统提供给驱动程序例程共享的资源,并且在任何时候要同步对物理设备的访 问 MT内核组件输出了一个机制,被称为自旋锁( spin lock),对称多处理器平台中一个或 多个正在运行的例程同时访问时,它被用来保护共享数据(或设备寄存器)。内核对自旋 锁的应用使用了两个策略: 在任何时刻,一个且仅一个例程能拥有一个特定的自旋锁:仅仅自旋锁的拥有者才能 访问它所保护的数据。另一个例程必须取得自旋锁后,才能访问相同的数据,并且 除非自旋锁的当前拥有者主动释放了它,否则其他任何例程都不能得到它。 象硬件和软件中断向量一样,内核为系统中的每一个自旋锁制定一个相关的IRL值。 内核模式例程只有运行在自旋锁指定的IRL上时,才能到特定的自旋锁。 这些策略用来保护某个驱动程序例程被更高优先级的驱动程序例程所抢占,前者通常是在 较低的IRL上运行,但是现在却拥有某个自旋锁,后者则希望获取同样的自旋锁,这通常 会造成死锁。 分配给某个自旋锁的IRL通常是能获得这个自旋锁的最高层IRQL例程的IRQL。一个最低 层驱动程序的ISR频繁地与驱动程序的DPC例程共享一个状态区,后者靠调用驱动程序提 供的临界区例程来访问共享区域。在这个例子中,保护共享区域的自旋锁拥有与 DIRQL相 同的IRL,设备中断通常发生在DIRL。当临界区例程拥有自旋锁并且在 DIRQL上访问共 享区域,ISR不能在单处理器机器中运行,这是因为设备中断被屏蔽,正如前面提到的那 样。在一个对程多处理器机器中,当临界区例程拥有自旋锁并且在 DIRQL访问共享数据 时,ISR还不能获得自旋锁以保护共享数据。 通过等待内核的调度者对象之一,一组内核模式线程能同步访问共享数据或资源。这些调 度对象有:事件、互斥体、信号量、定时器、或另一个线程。然而,大多数驱动程序不创 建它们自己的线程,因为当它们避免线程环境交换的同时,获得了更好的性能。对时间要 求比较苛刻的内核模式支持例程和驱动程序无论何时运行在 IRQL DISPATCH LEVEL或 DIRQL,都必须使用内核的自旋锁,从而来同步对共享数据和资源的访问。 想要得到更详细的信息,可以参看第16章“使用自旋锁和管理硬件优先级”,第3章“内 核调度对象” 1.3.5基于对象 Windows nt/ Windows2000是基于对象的系统。在执行体中,各种各样的组件定义一个或 多个对象类型。每个组件输出仅内核模式具有的支持例程,当这些例程被调用时,它们操 纵它的对象类型的实例。没有组件被允许直接访问另一个组件的对象类型的任何实例。每 个组件要想使用其他的组件对象,必须调用输出的支持例程
9 为了能在对称多处理器(SMP)平台上安全运行,任何操作系统都必须解决这样一个问题: 即,怎样保证正在某个处理器上运行的代码,不会同时访问和修改运行在另一个处理器上 的代码正在访问和修改的数据。例如,正在一个处理器上处理设备中断的最低层驱动程序 的 ISR 排斥其他对临界的、驱动程序定义的数据(或设备寄存器)的访问,以防它的设备 同时在另一个处理器上中断。 此外,在单一处理器的机器中被序列化的驱动程序 I/O 操作在 SMP 机器中能被同时执行。 这就是说,当一个处理输入 I/O 操作的驱动程序例程在一个处理器上被执行的时候,另一 个与设备通讯的例程能同时在另一个处理器上被执行。不论是运行在单处理器机器上,还 是 SMP 机器上,都要求包括 WDM 驱动程序在内的所有内核模式驱动程序能同步访问系统定 义的数据或系统提供给驱动程序例程共享的资源,并且在任何时候要同步对物理设备的访 问。 NT 内核组件输出了一个机制,被称为自旋锁(spin lock),对称多处理器平台中一个或 多个正在运行的例程同时访问时,它被用来保护共享数据(或设备寄存器)。内核对自旋 锁的应用使用了两个策略: ▪ 在任何时刻,一个且仅一个例程能拥有一个特定的自旋锁;仅仅自旋锁的拥有者才能 访问它所保护的数据。另一个例程必须取得自旋锁后,才能访问相同的数据,并且, 除非自旋锁的当前拥有者主动释放了它,否则其他任何例程都不能得到它。 ▪ 象硬件和软件中断向量一样,内核为系统中的每一个自旋锁制定一个相关的 IRQL 值。 内核模式例程只有运行在自旋锁指定的 IRQL 上时,才能到特定的自旋锁。 这些策略用来保护某个驱动程序例程被更高优先级的驱动程序例程所抢占,前者通常是在 较低的 IRQL 上运行,但是现在却拥有某个自旋锁,后者则希望获取同样的自旋锁,这通常 会造成死锁。 分配给某个自旋锁的 IRQL 通常是能获得这个自旋锁的最高层 IRQL 例程的 IRQL。一个最低 层驱动程序的 ISR 频繁地与驱动程序的 DPC 例程共享一个状态区,后者靠调用驱动程序提 供的临界区例程来访问共享区域。在这个例子中,保护共享区域的自旋锁拥有与 DIRQL 相 同的 IRQL,设备中断通常发生在 DIRQL。当临界区例程拥有自旋锁并且在 DIRQL 上访问共 享区域,ISR 不能在单处理器机器中运行,这是因为设备中断被屏蔽,正如前面提到的那 样。在一个对程多处理器机器中,当临界区例程拥有自旋锁并且在 DIRQL 访问共享数据 时,ISR 还不能获得自旋锁以保护共享数据。 通过等待内核的调度者对象之一,一组内核模式线程能同步访问共享数据或资源。这些调 度对象有:事件、互斥体、信号量、定时器、或另一个线程。然而,大多数驱动程序不创 建它们自己的线程,因为当它们避免线程环境交换的同时,获得了更好的性能。对时间要 求比较苛刻的内核模式支持例程和驱动程序无论何时运行在 IRQL DISPATCH_LEVEL 或 DIRQL,都必须使用内核的自旋锁,从而来同步对共享数据和资源的访问。 想要得到更详细的信息,可以参看第 16 章“使用自旋锁和管理硬件优先级”,第 3 章“内 核调度对象”。 1.3.5 基于对象 Windows NT/Windows 2000 是基于对象的系统。在执行体中,各种各样的组件定义一个或 多个对象类型。每个组件输出仅内核模式具有的支持例程,当这些例程被调用时,它们操 纵它的对象类型的实例。没有组件被允许直接访问另一个组件的对象类型的任何实例。每 个组件要想使用其他的组件对象,必须调用输出的支持例程
对这些约定的严格遵守使得 Window Nt/ Windows2000具有移植性和伸缩性。例如,操作系 统将来的某个版本能包含一个完全或部分被重写的内核组件,这个组件定义了相同的对象 类型,但是可能使用了完全不同的内部结构,却可以输出一组具有与现存组件所输出的例 程相同名字和参数的支持例程。这个假定的被重写的内核版本将对现存系统中任何其他的 执行组件的移植性没有任何影响。换句话说,操作系统组件没有使用后门通讯,并且驱动 程序也必须避免这种使用,以保持系统的移植性和可配置性 与操作系统相似,驱动程序及其设备也是基于对象的。包括用户模式代码在内,对于系统 中所有其他的组件,对某个设备的连接被描述成I/0管理器的文件对象之一的打开操作 在I/0系统中,每一个驱动程序的逻辑、虚拟、和/或物理设备被描述为设备对象。在I/0 管理器中,每一个驱动程序的加载映像被描述成一个驱动程序对象。I/0管理器为文件对 象、设备对象和驱动程序对象定义对象类型 像任何其他的可执行组件,驱动程序通过调用内核模式支持例程来使用对象,这些支持例 程是由Ⅰ0管理器和其他系统组件输出的。内核模式支持例程通常拥有明确的名称,指明 了每次操纵的特定对象和在这个对象上执行的操作。这些支持例程名称的形式如下: PrefixOperationOb ject 这里 Preti 指明了输出支持例程的内核模式组件,并且还通常指明了定义对象类型的组件。大多数前 缀有两个字母 Operation 描述对这个对象作什么。 Object 指明对象类型 例如, IoCreateDevice,在设备初始化过程中每个内核模式驱动程序都要调用这个例程 次或多次,从名字可以看出,这个例程创建一个设备对象,以描述一个物理、逻辑、或虚 拟设备,作为I/0请求的目标。 为了方便起见,一个系统组件能输出调用其他组件的支持例程。特别是,I/0管理器输出 特定例程,以简化驱动程序开发。例如, IoConnectInterrupt是一个最低层的驱动程序调 用,被用来注册它们的中断服务例程(ISR),它调用针对中断的内核的支持例程 在线DDK中的术语表包括了一组系统对象的定义,它们对驱动程序开发者非常有用。关于 对最低层和中间层驱动程序特别有用的支持例程的详细信息,参见巛 Windows2000驱动程 序开发参考》的卷1和卷2,以及在线DDK 1.36带有可复用IRP的包驱动I/0 Ⅰ/O管理器的主要工作是接收I/0请求(通常来自用户模式应用程序)、创建IRP以描述 它们、发送IRP到合适的驱动程序、跟踪它们直到它们被完成,还有返回状态到每个1/0 操作的初始请求者。I/O管理器、即插即用管理器和电源管理器使用IRP与包括WM驱动 程序在内的内核模式驱动程序通信,并且允许驱动程序之间相互通讯 注意,一些IRP可以被发送到多个驱动程序。例如,一个在某个磁碟上打开文件的请求可 能首先到达文件系统驱动程序,通过中间层镜像驱动程序,最终到达磁盘驱动程序,可能 是一个PnP硬件总线驱动程序。 因此,每个IRP有一个固定的部分和一个或多个驱动程序特定的I/0栈位置( stack
10 对这些约定的严格遵守使得 Window NT/Windows 2000 具有移植性和伸缩性。例如,操作系 统将来的某个版本能包含一个完全或部分被重写的内核组件,这个组件定义了相同的对象 类型,但是可能使用了完全不同的内部结构,却可以输出一组具有与现存组件所输出的例 程相同名字和参数的支持例程。这个假定的被重写的内核版本将对现存系统中任何其他的 执行组件的移植性没有任何影响。换句话说,操作系统组件没有使用后门通讯,并且驱动 程序也必须避免这种使用,以保持系统的移植性和可配置性。 与操作系统相似,驱动程序及其设备也是基于对象的。包括用户模式代码在内,对于系统 中所有其他的组件,对某个设备的连接被描述成 I/O 管理器的文件对象之一的打开操作。 在 I/O 系统中,每一个驱动程序的逻辑、虚拟、和/或物理设备被描述为设备对象。在 I/O 管理器中,每一个驱动程序的加载映像被描述成一个驱动程序对象。I/O 管理器为文件对 象、设备对象和驱动程序对象定义对象类型。 像任何其他的可执行组件,驱动程序通过调用内核模式支持例程来使用对象,这些支持例 程是由 I/O 管理器和其他系统组件输出的。内核模式支持例程通常拥有明确的名称,指明 了每次操纵的特定对象和在这个对象上执行的操作。这些支持例程名称的形式如下: PrefixOperationObject 这里 Prefix 指明了输出支持例程的内核模式组件,并且还通常指明了定义对象类型的组件。大多数前 缀有两个字母。 Operation 描述对这个对象作什么。 Object 指明对象类型。 例如,IoCreateDevice,在设备初始化过程中每个内核模式驱动程序都要调用这个例程一 次或多次,从名字可以看出,这个例程创建一个设备对象,以描述一个物理、逻辑、或虚 拟设备,作为 I/O 请求的目标。 为了方便起见,一个系统组件能输出调用其他组件的支持例程。特别是,I/O 管理器输出 特定例程,以简化驱动程序开发。例如,IoConnectInterrupt 是一个最低层的驱动程序调 用,被用来注册它们的中断服务例程(ISR),它调用针对中断的内核的支持例程。 在线 DDK 中的术语表包括了一组系统对象的定义,它们对驱动程序开发者非常有用。关于 对最低层和中间层驱动程序特别有用的支持例程的详细信息,参见《Windows 2000 驱动程 序开发参考》的卷 1 和卷 2,以及在线 DDK。 1.3.6 带有可复用 IRP 的包驱动 I/O I/O 管理器的主要工作是接收 I/O 请求(通常来自用户模式应用程序)、创建 IRP 以描述 它们、发送 IRP 到合适的驱动程序、跟踪它们直到它们被完成,还有返回状态到每个 I/O 操作的初始请求者。I/O 管理器、即插即用管理器和电源管理器使用 IRP 与包括 WDM 驱动 程序在内的内核模式驱动程序通信,并且允许驱动程序之间相互通讯。 注意,一些 IRP 可以被发送到多个驱动程序。例如,一个在某个磁碟上打开文件的请求可 能首先到达文件系统驱动程序,通过中间层镜像驱动程序,最终到达磁盘驱动程序,可能 是一个 PnP 硬件总线驱动程序。 因此,每个 IRP 有一个固定的部分和一个或多个驱动程序特定的 I/O 栈位置(stack