预计阅读本页时间:-
11.3 系统结构
前面的章节从用户态下程序员写代码的角度研究了Windows Vista系统。现在我们将观察系统是如何组织的,不同的部件承担什么工作以及它们彼此间或者和用户程序间是如何配合的。这是实现底层用户态代码的程序开发人员所能看见的操作系统部分,类似于子系统和本地服务,以及提供给设备驱动程序开发者的系统视图。
尽管有很多关于Windows使用方面的书籍,但很少有书讲述它是如何工作的。不过,查阅《Microsoft Windows Internals,4th ed》(Russionvich和Solomon,2004)是其中最好的选择之一。该书描述的虽然是Windows XP,但大部分的描述还是准确的。就内部机制而言,Windows XP和Windows Vista是非常相近的。
而且,微软通过Windows学术计划为大学教员和学生提供对其有帮助的Windows内核信息。该计划会发布大部分Windows Server 2003内核源代码、Cutler团队的原始NT设计文档和一大套源自Windows Internals书籍的表述资料。另外,Windows驱动工具也会提供大量内核工作信息,因为设备驱动器不仅使用I/O设备,还需要使用进程、线程、虚拟内存和进程间的通信等。
广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元
11.3.1 操作系统结构
Windows Vista操作系统包括很多层,如图11-6所示。在以下章节我们将研究操作系统中工作于内核态的最底级层次。其中心就是NOTS内核层自身,当Windows启动时由ntoskrnl.exe加载。NTOS包括两层,executive(执行体)提供大部分的服务,另一个较小的层称为内核(kernel),负责实现基础线程计划和同步抽象,同时也执行陷入句柄中断以及管理CPU的其他方面。
将NTOS分为内核和执行体体现了NT的VAX/VMS根源。VMS操作系统也是由Cutler团队设计的,可分为4个由硬件实施的层次:用户、管理程序、执行体和内核,与VAX处理机结构提供的4种保护模式一致。Intel CPU也支持这4种保护环,但是一些早期的NT处理机对此不支持,因此内核和执行体表现了由软件实施的抽象,同时VMS在管理者模式下提供的功能,如假脱机打印,NT是作为用户态服务提供的。
NT的内核态层如图11-13所示。NTOS的内核层在执行体层之上,因为它实现了从用户态到内核态转换的陷入和中断机制。图11-13所示的最顶层是系统库ntdll.dll,它实际工作于用户态。系统库包括许多为编译器运行提供的支持功能以及低级库,类似于UNIX中的libc。Ntdll.dll也包括了特殊码输入指针以支持内核初始化线程、分发异常和用户态的异步过程调用(Asynchronous Procedure Calls,APC)等。因为系统库对内核运行是必需的,所以每个由NTOS创建的用户态进程都具有相同固定地址描绘的ntdll。当NTOS初始化系统时,会创建一个局部目标并且记录下内核使用的ntdll输入指针地址。

在NTOS内核和执行体层之下是称为硬件抽象层(Hardware Abstraction Layer,HAL)的软件,该软件对类似于设备寄存器存取和DMA操作之类的底层硬件信息进行抽象,同时还就BIOS固件是如何表述配置信息和处理CPU芯片上的不同(如各种中断控制器)进行抽象。BIOS可以从很多公司获得,并且被集成为计算机母板上的永久内存。
内核态下另一个主要部件就是设备驱动器。Windows内核态下任何非NTOS或HAL的设备都会用到设备驱动器,包括文件系统、网络协议栈和其他如防病毒程序、DRM软件之类的内核扩展,以及与硬件总线接口的管理物理设备驱动器等。
I/O和虚拟内存部件协作加载设备驱动程序至内核存储器并将它们连接到NTOS和HAL层。I/O管理器提供发现、组织和操作设备的接口,包括安排加载适当的设备驱动程序等。大多数管理设备和驱动器的配置信息都保留在注册表的系统储巢中。I/O管理器的即插即用下层部件保留硬件储巢内检测出的硬件信息,该储巢是保留在内存中的可变储巢而非存在于硬盘中,系统每次引导都会重新创建。
以下将详细介绍操作系统的不同部件。
1.硬件抽象层
正如之前发布的基于NT的Windows系统一样,Windows Vista的目标之一是使得操作系统在不同的硬件平台之间具有可移植性。理想情况下,如果需要在一种新型计算机系统中运行该操作系统,仅仅需要在首次运行时使用新机器编译器重新编译操作系统即可。但实际上并没有那么简单。操作系统各层有大量部件具有很好的可移植性(因为它们主要处理支持编程模式的内部数据结构和抽象,从而支持特定的编成模式),其他层就必须处理设备寄存器、中断、DMA以及机器与机器间显著不同的其他硬件特征。
大多数NTOS内核源代码由C语言编写而非汇编语言(x86中仅2%是汇编语言,比x64少1%)。然而,所有这些C语言代码都不能简单地从x86系统中移植到一个SPARC系统,然后重新编译、重新引导,因为与不同指令集无关并且不能被编译器隐藏的处理机结构及其硬件有很多不同。像C这样的语言难以抽象硬件数据结构和参数,如页表输入格式、物理存储页大小和字长等。所有这些以及大量的特定硬件的优化即使不用汇编语言编写,也将不得不手工处理。
大型服务器的内存如何组织或者何种硬件同步基元是可获得的,与此相关的硬件细节对系统较高层都有比较大的影响。例如,NT的虚拟内存管理器和内核层了解涉及内存和内存位置的硬件细节。在整个系统中,NT使用的是比较和交换同步基元,对于没有这些基元的系统是很难移植上去的。最后,系统对字内的字节分类系统存在很多相关性。在所有NT原来移植到的平台上,硬件是设置为小端(little-endian)模式的。
除了以上这些影响便携性的较大问题外,不同制造商的不同母板还存在大量的小问题。CPU版本的不同会影响同步基元的实现方式。各种支持芯片组也会在硬件中断的优先次序、I/O设备寄存器的存取、DMA转换管理、定时器和实时时钟控制、多处理器同步、BIOS设备(如ACPI)的工作等方面产生差异。微软尝试通过最下端的HAL层隐藏对这些设备类型的依赖。HAL的工作就是对这些硬件进行抽象,隐藏处理器版本、支持芯片集和其他配置变更等具体细节。这些HAL抽象展现为NTOS和驱动可用的独立于机器的服务。
使用HAL服务而不直接写硬件地址,驱动器和内核在与新处理器通信时只需要较小改变,而且在多数情况下,尽管版本和支持芯片集不同但只要有相同的处理器结构,系统中所有部件均无需修改就可运行。
HAL对诸如键盘、鼠标、硬盘等特殊的I/O设备或内存管理单元不提供抽象或服务。这种抽象功能广泛应用于整个内核态的各部件,如果没有HAL,通信时即使硬件间很小的差异也会造成大量代码的重大修改。HAL自身的通信很简单,因为所有与机器相关的代码都集中在一个地方,移植的目标就很容易确定:即实现所有的HAL服务。很多版本中,微软都支持HAL扩展工具包,允许系统制造者生产各自的HAL从而使得其他内核部件在新系统中无需更改即可工作,当然这要在硬件更改不是很大的前提下。
通过内存映射I/O与I/O端口的对比可以更好地了解硬件抽象层是如何工作的。一些机器有内存映射I/O,而有的机器有I/O端口。驱动程序是如何编写的呢?是不是使用内存映射I/O?无需强制做出选择,只需要判断哪种方式使驱动程序可独立于机器运行即可。硬件抽象层为驱动程序编写者分别提供了三种读、写设备寄存器的程序:
uc=READ_PORT_UCHAR(port);WRITE_PORT_UCHAR(port,uc);
us=READ_PORT_USHORT(port);WRITE_PORT_USHORT(port,us);
ul=READ_PORT_ULONG(port);WRITE_PORT_ULONG(port,ul);
这些程序各自在指定端口读、写无符号8、16、32位整数,由硬件抽象层决定这是否需要内存映射I/O。这样,驱动程序可以在设备寄存器实现方式有差异的机器间使用而不需要修改。
驱动程序会因为不同目的而频繁存取特定的I/O设备。在硬件层,一个设备在确定的总线上有一个或多个地址。因为现代计算机通常有多个总线(ISA、PCI、PCI-X、USB、1394等),这就可能造成不同总线上的多个设备有相同的地址,因此需要一些方法来区别它们。HAL把与总线相关的设备地址映射为系统逻辑地址并以此来区分设备。这样,驱动程序就无需知道何种设备与何种总线相关联。这种机制也保护了较高层避免进行总线结构和地址规约的交替。
中断也存在相似的问题——总线依赖性。HAL同样提供服务在系统范围内命名中断,并且允许驱动程序将中断服务程序附在中断内而无需知道中断向量与总线的关系。中断请求管理也受HAL控制。
HAL提供的另一个服务是在设备无关方式下建立和管理DMA转换,对系统范围和专用I/O卡的DMA引擎进行控制。设备由其逻辑地址指示。HAL实现软件的散布/聚合(从不相邻的物理内存块的地方写或者读)。
HAL也是以用一种可移植的方式来管理时钟和定时器的。定时器是以100纳秒为单位从1601年1月1日开始计数的,因为这是1601年的第一天,简化了闰年的计算。(一个简单测试:1800年是闰年吗?答案:不是。)定时器服务和驱动程序中的时钟运行的频率是解耦合的。
有时需要在底层实现内核部件的同步,尤其是为了防止多处理机系统中的竞争环境。HAL提供基元管理同步,如旋转锁,此时一个CPU等待其他CPU释放资源,比较特殊的情况是资源被几个机器指令占有。
最终,系统引导后,HAL和BIOS通信,检查系统配置信息以查明系统所包含的总线、I/O设备及其配置情况,同时该信息被添加进注册表。HAL工作情况摘要如图11-14所示。

2.内核层
在硬件抽象层之上是NTOS,包括两层:内核和执行体。“内核”在Windows中是一个易混淆的术语。它可以指运行在处理机内核态下的所有代码,也可以指包含了Windows操作系统核心NTOS的ntoskrnl.exe文件,还可以指NTOS里的内核层,在本章中我们使用这个概念。此外,“内核”甚至用来命名用户态下提供本地系统调用的封装器的Win32库:kernel32.dll。
Windows操作系统的内核层(如图11-13所示,执行体之上)提供了一套管理CPU的抽象。最核心的抽象是线程,但是内核也实现了异常处理、陷阱以及各种中断。支持线程的数据结构的创建和终止是在执行体实现的。核心层负责调度和同步线程。在一个单独的层内支持线程,允许执行体在用户态下,可以通过使用用来编写并行代码且相同优先级的多线程模型来执行,但同步原语的执行更专业。
内核线程调度程序负责决定哪些线程执行在系统的每一个CPU上。线程会一直执行,直到产生了一个定时器中断,或者是当线程需要等待一些情况,比如等待一个I/O读写完成或是一个锁定被释放,或者是更高优先级的线程等待运行而需要CPU,这时正在执行的线程会切换到另一个线程(量子过期)。当一个线程向另一个线程转换时,调度程序会在CPU上运行,并确保寄存器及其他硬件状态已保存。然后,调度程序会选择另一个线程在CPU上运行,并且恢复之前所保存的最后一个线程的运行状态。
如果下一个运行的线程是在一个不同的地址空间(例如进程),调度程序也必须改变地址空间。详细的调度算法我们将在本章内谈到进程和线程时讨论。
除了提供更高级别的硬件抽象和线程转换机制,核心层还有另外一项关键功能:提供对下面两种同步机制低级别的支持:control对象和dispatcher对象。Control对象,是核心层向执行体提供抽象的CPU管理的一种数据结构。它们由执行体来分配,但由核心层提供的例程来操作。Dispatcher对象是一种普通执行对象,使用一种公用的数据结构来同步。
3.延迟过程调用
Control对象包括线程、中断、定时器、同步、调试等一些原语对象,和两个用来实现DPC和APC的特殊对象。DPC(延迟过程调用)对象是用来减少执行ISR(中断服务例程)所需要的时间,以响应从特定的设备来的中断。
系统硬件为中断指定了硬件优先级。在CPU进行工作时也伴随着一个优先级。CPU只响应比当前更高优先级的中断。通常的优先级是0,包括所有用户态下的优先级。设备中断发生在优先级3或更高,让一个设备中断的ISR以同一优先级的中断来执行是防止其他不重要的中断影响它正在进行的重要中断。
如果ISR执行得太长,提供给低优先级中断的服务将被推迟,可能造成数据丢失或减缓系统的I/O吞吐量。多ISR可以在任何同一时刻处理,每一个后续的ISR是由于产生了更高优先级的中断。
为了减少处理ISR所花费的时间,只有关键的操作才执行,如I/O操作结果的捕捉和设备重置。直到CPU的优先级降低,且没有其他中断服务阻塞,才会进行下一步的中断处理。DPC对象用来表示将要做的工作,ISR调用核心层排列DPC到特定处理器上的DPC队列。如果DPC在队列的第一个位置,内核会登记一个特殊的硬件请求让CPU在优先级2产生中断(NT下称为DISPATCH级别)。当最后一个执行的ISR完成后,处理器的中断级别将回落到低于2,这将解开DPC处理中断。服务于DPC中断的ISR将会处理内核排列好的每一个DPC对象。
利用软中断延迟中断处理是一种行之有效的减少ISR延迟时间的方法。UNIX和其他系统在20世纪70年代开始使用延迟处理,以处理缓慢的硬件和有限的缓冲串行连接终端。ISR负责处理从硬件提取字符并排列它们。在所有高级别的中断处理完成以后,软中断将执行一个低优先级的ISR做字符处理,比如通过向终端发送控制字符来执行一个退格键,以抹去最后一个显示字符并向后移动光标。
在当前的Windows操作系统下,类似的例子是键盘设备。当一个键被敲击以后,键盘ISR从寄存器中读取键值,然后重新使键盘中断,但并不对下一步的按键进行及时处理。相反,它使用一个DPC去排队处理键值,直到所有优先的设备中断已处理完成。
因为DPC在级别2上运行,它们并不干涉ISR设备的执行,在所有排队中的DPC执行完成并且CPU的优先级低于2之前,它们会阻止任何线程的运行。设备驱动和系统本身必须注意不要运行ISR或DPC太长时间。因为在运行它们的时候不能运行线程,ISR或DPC的运行会使系统出现延迟,并且可能在播放音乐时产生不连续,因为拖延了线程对声卡的音乐缓冲区的写操作。DPC另一个通常的用处是运行程序以响应定时器中断。为了避免线程阻塞,要延长运行时间的定时器事件需要向内核维持后台活动的线程工作池做排队请求。这些线程有调度优先级12、13或15。我们会在线程调度部分看到,这些优先级意味着工作项目将会先于大多数线程执行,但是不会打断实时线程。
4.异步过程调用
另一个特殊的内核控制对象是APC(异步过程调用)对象。APC与DPC的相同之处是它们都是延迟处理系统例行程序,不同之处在于DPC是在特定的CPU上下文中执行,而APC是在一个特定的线程上下文中执行。当处理一个键盘敲击操作时,DPC在哪一个上下文中运行是没有关系的,因为一个DPC仅仅是处理中断的另一部分,中断只需要管理物理设备和执行独立线程操作,例如在内核空间的一个缓冲区记录数据。
当原始中断发生时,DPC例程运行在任何线程的上下文中。它利用I/O系统来报告I/O操作已经完成,I/O系统排列一个APC在线程的上下文中运行从而做出原始的I/O请求,在这里它可以访问处理输入的线程的用户态地址空间。
在下一个合适的时间,内核层会将APC移交给线程而且调度线程运行。一个APC被设计成看上去像一个非预期的程序调用,有些类似于UNIX中的信号处理程序。不过在内核态下,内核态的APC为了完成I/O操作,而在完成初始化I/O操作的线程的上下文中执行。这使APC既可以访问内核态的缓冲区,又可以访问用户态下,属于包含线程的进程的地址空间。一个APC在什么时候被移交,取决于线程已经在做什么,以及系统的类型是什么。在一个多处理器系统中,甚至是在DPC完成运行之前,接收APC的线程才可以开始执行。
用户态下的APC也可以用来把用户态的I/O操作已经完成的信息,通知给初始化I/O操作的线程。但只有当内核中的目标线程被阻塞和被标示为准备接收APC时,用户态下的APC才可调用用户态下的应用程序。但随着用户态堆栈和寄存器的修改,为了执行在ntdll.dll系统库中的APC调度算法,内核将等待中的线程中断,并返回到用户态。APC调度算法调用和I/O操作相关的用户态应用程序。除了一些I/O完成后,作为一种执行代码方法的用户态下的APC外,Win32 API中的QueueUserAPC允许将APC用于任意目的。
执行体也使用除了I/O完成之外的一些APC操作。由于APC机制精心设计为只有当它是安全的时候才提供APC,它可以用来安全地终止线程。如果这不是一个终止线程的好时机,该线程将宣布它已进入一个临界区,并延期交付APC直至得到许可。在获得锁或其他资源之前,内核线程会标记自己已进入临界区并延迟APC,这时,它们不能被终止,并仍然持有资源。
5.调度对象
另一种同步对象是调度对象。这是常用的内核态对象(一种用户可以通过句柄处理的类型),它包含一个称为dispatcher_header的数据结构,如图11-15所示。

它们包括信号器、互斥体、事件、可等待定时器和其他一些可以等待其他线程同步执行的对象。它们还包括表示打开的文件的对象、进程、线程和IPC端口。调度数据结构包含了表示对象状态的标志,和等待被标记的对象的线程队列。
同步原语,如信号器,是标准的调度对象。另外定时器、文件、端口线程和进程使用调度对象机制去通知。当一个定时器开启、一个文件I/O完成、一个端口正在传输数据或是一个线程或进程终止时,相关的调度对象会被通知,并唤醒所有等待该事件的线程。
由于Windows使用了一个单一的标准机制去同步内核态对象,一些专门的API就无需再等待事件,例如在UNIX中用来等待子进程的wait3。而通常情况下,线程要一次等待多个事件。在UNIX中,通过“select”系统调用,一个进程可以等待任何一个64位网络接口可以获得的数据。在Windows中亦有一个类似的APIWaitForMultipleObjects,但是它允许一个线程等待任何类型的有句柄的调度对象。超过64个句柄可以指定WaitForMultipleObjects,以及一个可选择的超时值。线程随时准备运行任何一个和句柄标记相关的事件或发生超时。
内核使用两个不同的程序使得线程等待调度对象运行。发出一个通知对象信号使每一个等待的线程可以运行。同步对象仅使第一个等待的线程可以运行,用于调度对象,实施锁元,如互斥体。当一个线程等待一个锁再次开始运行,它做的第一件事就是再次尝试请求锁。如果一次仅有一个线程可以保留锁,其他所有可运行的线程可能立刻被阻塞,从而产生许多不必要的现场交换。使用同步机制和使用通知机制的分派对象(dispatcher object)之间的差别是dispatcher_header结构中的一个标记。
另外,在Windows代码中互斥体称为“变体”(mutant)。因为当一个线程保留一个出口时,它们需要执行OS/2语义中的非自动解锁,看来这是Cutler奇特的考虑。
6.执行体
如图11-13所示,在NTOS的内核层以下是执行体。执行体是用C语言编写的,在结构上最为独立(内存管理是一个明显的例外),并且经过少量的修改已经移植到新的处理器上(MIPS、x86、PowerPC、Alpha、IA64和x64)。执行体包括许多不同的组件,所有的组件都通过内核层提供的抽象控制器来运行。
每个组件分为内部和外部的数据结构和接口。每个组件的内部方法是隐藏的,只有组件自己可以调用,而外部方法可以由执行体的所有其他组件调用。外部接口的一个子集由一个ntoskrnl.exe提供,而且设备驱动可以链接到它们就好像执行体是一个库。微软称许多执行体组件为“管理器”,因为每一个组件管理操作系统的一部分,例如I/O、内存、进程、对象等。
对于大多数操作系统而言,许多功能在Windows上执行就像库的编码。除非在内核方式下运行,它的数据结构可以被共享和保护,以避免用户态下的编码访问,因此它具有硬件状态的访问权限,例如MMU控制寄存器。但是另一方面,执行体只是代表它的调用者简单执行操作系统的函数,因此它运行在它的调用者的线程中。
当任何执行控制操作阻塞等待与其他线程同步时,用户态线程也会阻塞。这在为一个特殊的用户态线程工作时是有意义的,但是在做一些相关的内务处理任务时是不公平的。当执行体认为一些内务处理线程是必须的时候,为了避免劫持当前的线程,一些内核态线程就会具体于特定的任务而产生,例如确保更改了的页会被回写到硬盘上。
对于可预见的低频率任务,会有一个线程一秒运行一次而且由一个长的项目单来处理。对于不可预见的工作,有一个之前曾经提到的高优先级的辅助线程池,通过将队列请求和发送辅助线程等待的同步事件信号,可以用来运行有界任务。
对象管理器管理在执行体使用的大部分内核态对象,包括进程、线程、文件、信号、I/O设备及驱动、定时器等。就像之前提到的,内核态对象仅仅是内核分配和使用的数据结构。在Windows中,内核数据结构有许多共同特点,即它们在管理标准功能中特别有用。
这些功能由对象管理器提供,包括管理对象的内存分配和释放,配额计算,支持通过句柄访问对象,为内核态指针引用保留引用计数,在NT名字空间给对象命名,为管理每一个对象的生命周期提供可扩展的机制。需要这些功能的内核数据结构是由对象管理器来管理的。其他数据结构,例如内核层使用的控制对象,或仅仅是内核态对象的扩展对象,不由对象管理器管理。
对象管理器的每一个对象都有一个类型用来指定这种类型的对象的生命周期怎样被管理。这些不是面向对象意义中的类型,而仅仅是当对象类型产生时的一个指定参数集合。为了产生一个新的类型,一个操作元件只需要调用一个对象管理器API即可。对象在Windows的函数中很重要,在下面的章节中将会讨论有关对象管理器的更多细节。
I/O管理器为实现I/O设备驱动提供了一个框架,同时还为设备上的配置、访问和完成操作提供一些特定的运行服务。在Windows中,设备驱动器不仅仅管理硬件设备,它们还为操作系统提供可扩展性。在其他类型的操作系统中被编译进内核的功能是被Windows内核动态装载和链接的,包括网络协议栈和文件系统。
最新的Windows版本对在用户态上运行设备驱动程序有更多的支持,这对新的设备驱动程序是首选的模式。Windows Vista有超过100万不同的设备驱动程序,工作着超过了100万不同的设备。这就意味着要获取正确的代码。漏洞导致设备在用户态的进程中崩溃而不能使用,这比造成对系统进行检测错误要好得多。错误的内核态设备驱动是导致Windows可怕的BSOD(蓝屏死机)的主要来源,它是Windows侦测到致命的内核态错误并关机或重新启动系统。蓝屏死机可以类比于UNIX系统中的内核恐慌。
在本质上,微软现在已经正式承认那些在microkernels研究领域的如MINIX 3和L4的研究员多年来都知道的结果:在内核中有更多的代码,那么内核中就有更多缺陷。由于设备驱动程序占了70%的内核代码,更多的驱动程序可以进入用户态进程,其中一个bug只会触发一个单一驱动器的失败(而不是降低整个系统)。从内核到用户态进程的代码移动趋势将在未来几年加速发展。
I/O管理器还包括即插即用和电源管理设施。当新设备在系统中被检测到,即插即用就开始工作。该即插即用设备的子模块首先被通知。它与服务一起工作,即用户态即插即用管理器,找到适当的设备驱动程序并加载到系统中。找到合适的设备驱动程序并不总是很容易,有时取决于先进的匹配具体软件设备特定版本的驱动程序。有时一个单一的设备支持一个由不同公司开发的多个驱动程序所支持的标准接口。
电源管理能降低能源消耗,延长笔记本电脑电池寿命,保存台式电脑和服务器能量。正确使用电源管理是具有挑战性的,因为在把设备和buses连接到CPU和内存时有许多微妙的依赖性。电力消耗不只是由设备供电时的影响,而且还由CPU的时钟频率影响,这也是电源管理在控制。
我们会在11.7节对I/O进一步研究和以及在11.8节中介绍最重要的NT文件系统NTFS。
进程管理管理着进程和线程的创建和终止,包括建立规则和参数指导它们。但是线程运行方面由核心层决定,它控制着线程的调度和同步,以及它们之间相互控制的对象,如APC。进程包含线程、地址空间和一个可以用来处理进程指定内核态对象的句柄表。进程还包括调度器进行地址空间交换和管理进程中的具体硬件信息(如段描述符)所需要的信息。我们将在11.4节研究进程和线程的管理。
执行内存管理器实现了虚拟内存架构的需求分页。它负责管理虚拟页映射到物理页帧,管理现有的物理帧,和使用备份管理磁盘上页面文件,这些页面文件是用来备份那些不再需要加载到内存中的虚拟页的私有实例。该内存管理器还为大型服务器应用程序提供了特殊功能,如数据库和编程语言运行时的组件,如垃圾收集器。我们将在11.5节中研究内存管理。
内存管理器优化I/O的性能,文件系统内核虚拟地址空间保持一个内存的文件系统页。内存管理器使用虚拟的地址进行缓存,也就是说,按照它们文件所在位置来组织缓存页。这不同于物理块内存,例如在UNIX中,系统为原始磁盘卷保持一个物理地址块的内存。
内存的管理是使用内存映射文件来实现的。实际的缓存是由内存管理器完成。内存管理器需要关心的只是文件的哪些部分需要内存,以确保缓存的数据即时地刷新到磁盘中,并管理内核虚拟地址映射缓存文件页。如果一个页所需的I/O文件在缓存中没有,该页在使用内存管理器时将会发生错误。我们会在11.6节中学习内存管理器。
安全引用监视器(security reference monitor)执行Windows详细的安全机制,以支持计算机安全要求的国际标准的通用标准(Common Critieria),一个由美国国防部的橘皮书的安全要求发展而来的标准。这些标准规定了一个符合要求的系统必须满足的大量规则,如登录验证、审核、零分配的内存等更多的规则。一个规则要求,所有进入检查都由系统中的一个模块进行检查。在Windows中此模块就是内核中的安全监视器。我们将在11.9节中更详细地学习安全系统。
执行体中包括其他一些组件,我们将简要介绍。如前所述,配置管理实现注册表的执行组件。注册表中包含系统配置数据的文件的系统文件称为储巢(hive)。最关键的储巢是系统启动时加载到内存的系统储巢。只有在执行体成功地初始化其主要组件,包括了系统磁盘的I/O驱动程序,之后才是文件系统中储巢关联的内存中的储巢副本。因此,如果试图启动系统时发生不测,磁盘上的副本是不太可能被损坏的。
LPC的组成部分提供了运行在同一系统的进程之间的高效内部通信。这是一个基于标准的远程过程调用(RPC)功能,实现客户机/服务器的处理方式的数据传输。RPC还使用命名管道和TCP/IP作为传输通道。
在Windows Vista(现在称为ALPC、高级LPC)中LPC大大加强了对RPC新功能的支持,包括来自内核态组件的RPC,如驱动。LPC是NT原始设计中的一个重要的组成部分,因为它被子系统层使用,实现运行在每个进程和子系统进程上库存例程的通信,这实现了一个特定操作系统的个性化功能,如Win32或POSIX。
Windows NT 4.0中的许多代码与Win32进入内核的图形界面相关,因为当时的硬件无法提供所需的性能。该代码以前位于csrss.exe子系统进程,执行Win32接口。以内核为基础的图形用户界面的代码位于一个专门的内核驱动win32k.sys中。这一变化预计将提高Win32的性能,因为额外的用户态/内核态的转换和转换地址空间的成本经由LPC执行通信是被清除的。但并没能像预期的那样取得成功,因为运行在内核中的代码要求是非常严格的,运行在内核态上的额外消耗抵消了因减少交换成本获得的收益。
7.设备驱动程序
最后一部分图11-13是设备驱动程序的组成。在Windows中的设备驱动程序的动态链接库是由NTOS装载。虽然它们主要是用来执行特定硬件的驱动程序,如物理设备和I/O总线,设备驱动程序的机制也可作为内核态的一般可扩展性的机制。如上所述,大部分的Win32子系统是作为一个驱动程序被加载。
I/O管理器组织的数据按照一定的路线流经过每个设备实例,如图11-16。这个路线称为设备栈,由分配到这条路线上的内核设备对象的私有实例组成。设备堆栈中的每个设备对象与特定的驱动程序对象相关联,其中包含日常使用的I/O请求的数据包流经该设备堆栈的表。在某些情况下,堆栈中的设备驱动程序表示其唯一的目的是在某一特定的设备上过滤I/O操作目标、总线或网络驱动器。过滤器的使用是有一些原因的。有时预处理或后处理I/O操作可以得到更清晰的架构,而其他时候只是以实用为出发点,因为没有修改驱动的来源和权限,过滤器是用来解决这个问题的。过滤器还可以全面执行新的功能,如把磁盘分区或多个磁盘分成RAID卷。

文件系统作为驱动程序被加载。每个文件系统卷的实例,有一个设备对象创建,并作为该设备堆栈卷的一部分。这是设备对象将与驱动对象的文件系统适当的卷格式发生关联。特别过滤驱动程序,称为文件系统过滤驱动程序,可以在插入设备对象之前,文件系统设备对象将功能应用于被发送到每个卷的I/O请求,如数据读取或写入的病毒检查。
网络协议也作为使用I/O模型的驱动被装载起来,例如Windows Vista整合的IPv4/IPv6 TCP/IP实现。对于老的基于MS-DOS的Windows操作系统,TCP/IP驱动实现了一个特殊的Windows I/O模型网络接口上的协议。还有其他一些驱动也执行这样的安排,其中的Windows小型端口。共享功能是在一个类驱动程序中。例如,SCSI或IDE磁盘或USB设备通用功能是作为一类驱动提供的,这一类驱动为这些设备的每个特定类型提供微端口驱动程序连接为一个库。
我们在本章不讨论任何特定的设备驱动,但是在11.7节中将更为详细地介绍有关I/O管理器如何与设备驱动互动。