分类: WINDOWS
2008-01-12 14:28:37
2.3 总体结构
在这一节 关于Windows设计目标和包装方式的简短介绍中,我们先来看一看Windows总体结构中的关键系统组件。一个简化版本的总体结构如图2.1所示。请 记住,这个图只是一个基本结构,它并没有显示任何特定内容(例如,网络组件和各种类型设备驱动程序的层次并没有显示)。
图2.1 简化的Windows结构图
在图2.1中,首先可注意到中间有一条线把Windows操作系统的用户模式(user mode)和内核模式(kernel mode) 两部分划分开来。线上面的方框代表了用户模式的进程,线之下的组件是内核模式的操作系统服务。正如在第1章中曾经提到的,用户模式的线程在一个受保护的进 程地址空间中执行(不过,当它们在内核模式中执行的时候,它们可以访问系统空间)。因此,系统支持进程、服务进程、用户应用程序和环境子系统都有它们各自 的私有进程地址空间。
四种基本的用户模式进程分别描述如下。
n 固定的(或者硬性指定的)系统支持进程(system support processes),比如登录(logon)进程和会话管理器(session manager),它们并不是Windows的服务。也就是说,它们不是由服务控制管理器来启动的(第4章将会详细地介绍Windows服务);
n 服务进程(service processes) 宿纳(host)的是Windows服务,比如任务调度器(Task Scheduler)和假脱机服务。Windows服务的运行通常要独立于用户登录。许多Windows服务器应用,比如Microsoft SQL Server和Microsoft Exchange Server,也包含了一些以Windows服务方式来运行的组件;
n 用户应用程序(user applications)有六种类型:Windows 32位、Windows 64位、Windows 3.1 16位、MS-DOS 16位、POSIX 32位或者OS/2 32位;
n 环境子系统服务器进程(environment subsystem server processes)实现了操作系统环境(environment) 的支持部分。这里所谓的环境是指操作系统展示给用户或者程序员的个性化部分。Windows NT最初发布的时候带了三个不同的环境子系统:Windows、POSIX和OS/2。然而,Windows 2000是最后一次带OS/2发布,之后OS/2就被去掉了。到了Windows XP,在基本的产品中只有Windows子系统随产品一起发布——不过,一个增强的POSIX子系统也可以使用,它是针对Unix产品的免费服务的一部 分。
在 图2.1中,请注意在“服务进程”和“用户应用程序”方框下面的“子系统DLL”方框。在Windows下,用户应用程序并不直接调用原始的 Windows操作系统服务,相反,它们通过一个或者多个子系统动态链接库(DLLs)来发起调用。子系统DLL的角色是,将一个已文档化的函数转化为一 些恰当的内部(通常是未文档化的)Windows系统服务调用。这个转化过程可能会——也可能不会——向正在为用户应用程序提供服务的环境子系统进程发送 一个消息。
Windows的内核模式组件包含以下部分。
n Windows执行体(executive)包含了基本的操作系统服务,比如内存管理、进程和线程管理、安全性、I/O、网络和跨进程通信。
n Windows内核(kernel)是由一组低层次的操作系统功能构成的,比如线程调度(thread scheduling)、中断(interrupt)和异常分发(exception dispatching),以及多处理器同步。它也提供了一组例程和基本对象。执行体的其余部分利用这些例程和对象实现更高层次的功能。
n 设备驱动程序(device drivers)既包括硬件设备驱动程序,也包括文件系统和网络驱动程序。其中硬件设备驱动程序将用户的I/O函数调用转换成特定的硬件设备I/O请求。
n 硬件抽象层(HAL,Hardware Abstraction Layer)是指一层特殊的代码,它把内核、设备驱动程序和Windows执行体的其余部分,跟与平台相关的硬件差异(比如不同主板的差异)隔离开来。
n 窗口和图形系统(windowing and graphic system)实现了图形用户界面(GUI)函数(更为人们熟知的叫法是Windows USER和GDI函数),比如对窗口的处理、用户界面控件,以及绘制等。
表2.1列出了Windows操作系统核心组件的文件名。(你有必要知道这些文件名,因为我们将会用名字来引用某些系统文件)。本章后面部分以及后续的章节中将会详细地介绍其中的每一个组件。
表2.1 Windows的核心系统文件
文件名 |
组 件 |
Ntoskrnl.exe |
执行体和内核 |
Ntkrnlpa.exe (仅用于32位系统) |
执行体和内核,支持物理地址扩展(PAE),使得系统可寻址多达64GB物理内存 |
Hal.dll |
硬件抽象层 |
Win32k.sys |
Windows子系统的内核模式部分 |
Ntdll.dll |
内部支持函数,以及执行体函数的系统服务分发存根(stub) |
Kernel32.dll, Advapi32.dll, |
Windows的核心子系统DLL |
在探讨这些系统组件的细节之前,我们先来看一看,Windows如何实现跨多种硬件体系结构的可移植性。
可移植性
Windows 的一个设计目标是,要能够运行在各种不同的硬件体系结构之上,包括基于Intel的CISC系统以及RISC系统。Windows的初始发行版本支持 x86和MIPS体系结构。稍后又加入了对DEC(Digital Equipment Corporation,该公司被Compaq购买了,而Compaq后来又与HP即Hewlett Packard兼并了) Alpha AXP的支持(尽管Alpha AXP是一个64位处理器,但Windows NT运行在32位模式下。在Windows 2000的开发过程中,一个原生的64位版本可以在Alpha AXP上运行,但是这个版本没有被发布)。在Windows 3.51中又加入了对第四种处理器结构Motorola PowerPC的支持。然而,由于市场需求的变化,在Windows 2000开发前夕,对MIPS和PowerPC体系结构的支持被放弃了。后来,Compaq又撤消了对Alpha AXP体系结构的支持,所以最终的结果是,Windows 2000只支持x86体系结构。在最新的Windows版本,即Windows XP和Windows Server 2003中,新增加了对三种64位处理器族的支持,它们分别是Intel Itanium IA-64族、AMD x86-64族,以及Intel针对x86的64位扩展技术(EM64T,它与AMD x86-64体系结构相兼容,但是在所支持的指令方面有略微的差别)。后两种处理器族称为64位扩展系统(64-bit extended systems),在本书中引用为x64(关于Windows如何在64位版本中运行32位应用程序,第3章将会给出解释)。
Windows主要通过以下两种方法来实现可移植性,以支持多种硬件体系结构和平台。
n Windows有一个分层设计,系统的低层部分是与处理器体系结构相关的,或者是与平台相关的,这些部分被隔离到独立的模块中,所以,系统的高层部分可以 不考虑体系结构之间的差别,也不用关心硬件平台上的差异。有两个关键的组件为操作系统提供了可移植性,它们是内核(包含在Ntoskrnl.exe)和硬 件抽象层(或称HAL,包含在Hal.dll中)。本章后面将会详细地描述这两个组件。与体系结构相关的功能,比如线程环境切换(thread context switching)和陷阱分发(trap dispatching)是在内核中实现的。在同样的体系结构中,不同系统之间有所差异的功能(例如,不同的主板)则是在HAL中实现的。还有另外一个组 件中也有相当数量的代码是与体系结构相关的,那就是内存管理器,但是与整个系统相比,这仍然只是一小部分而已。
n Windows的绝大部分代码是用C语言编写的,少部分是用C++编写的。只有那些需要直接与系统硬件进行通信的部分(比如中断陷阱处理器, interrupt trap handler),或者对性能极端敏感的操作系统部分(比如环境切换,context switching),才是用汇编语言编写的。不仅在内核和HAL中有汇编语言代码,而且在核心操作系统的其他一些地方也有汇编语言代码(比如实现了互锁 指令的例程,以及本地过程调用设施中的一个模块),既可能在Windows子系统的内核模式部分,甚至也可能在某些用户模式库中,比如Ntdll.dll 中的进程启动代码(本章后面将会介绍系统库Ntdll.dll)。
对称多处理
多任务(multitasking) 是指在多个执行线程之间共享同一个处理器的操作系统技术。然而,当一台计算机有不止一个处理器的时候,它可以同时执行两个线程。因此,虽然一个多任务操作 系统只不过看起来好像同一时刻可以执行多个线程,但是,一个多处理操作系统可以真正地做到同一时刻执行多个线程,在每个处理器上执行一个线程。
正如本章开始时所提到的,Windows的一个关键设计目标是,它必须能够很好地在多处理器计算机系统上运行。Windows是一个对称多处理(SMP,symmetric multiprocessing)操作系统。在这些处理器中没有主处理器——操作系统和用户线程可以被调度到任何一个处理器上运行。而且,所有的处理器共享惟一的内存空间。这种模型与非对称多处理(ASMP,asymmetric multiprocessing)不同,在一个典型的非对称多处理系统中,操作系统选择其中一个处理器来执行操作系统内核代码,而其他的处理器只运行用户代码。这两种多处理模型的区别如图2.2所示。
图2.2 对称和非对称多处理
Windows XP和Windows Server 2003支持两种新的多处理器系统:超线程(hyperthreading)和NUMA(非一致的内存结构,non-uniform memory architecture)。在下面的段落中将会简要地介绍这两种系统(有关Windows对这些系统的调度支持的详细描述,请参见第6章中线程调度一节)。
超线程是 Intel引入的一项技术,它可以在一个物理处理器上提供多个逻辑处理器。每个逻辑处理器有它自己的CPU状态,但是执行引擎和芯片上的高速缓存则是共享 的。这使得一个逻辑CPU可以在其他逻辑CPU正忙着(比如正在执行中断处理工作,从而阻止了在这个逻辑处理器上运行线程)的时候继续执行。 Windows XP的调度算法已经被改进过了,它可以最佳地利用多处理器超线程机器,例如,原来的做法是,将线程调度到一个空闲的物理处理器上,现在改进为“选择一个物 理处理器上的空闲逻辑处理器(该处理器的其他逻辑处理器可能正在忙着)”。
在非一致 的内存结构NUMA系统中,处理器被组织成更小的单元,称为节点(nodes)。每个节点有它自己的处理器和内存,并且通过一个缓存一致(cache- coherent)的互联总线连接到更大的系统上。NUMA系统上的Windows仍然作为一个SMP系统来运行,其中所有的处理器都可以访问所有的内存 ——只不过,节点本地的内存访问起来比其他节点的内存更快一些而已。系统想要提高性能的做法是,根据线程用到的内存所在的节点,将线程调度到同一节点中的 处理器上。这种做法的目标是,尽可能在节点内部满足内存申请的请求,只有在必要的时候才从其他的节点分配内存。
虽然 Windows最初的设计只能支持最多32个处理器,但是,在多处理器设计中,并没有本质的因素限制处理器的个数最多为32——很显然,此数字只是为了方 便而引入的一个限制,因为32个处理器可以很容易地使用一个原生的32位数据类型作为位掩码来表示。实际上,Windows的64位版本可以最多支持64 个处理器,因为在64位机器上,一个字的原生大小是64位。
能支持的处理器的实际数目取决于所用的Windows的版本(参见表2.3和2.4)。该数据被存放在注册表值HKLM\SYSTEM\CurrentControlSet\Control\Session\Manager\ LicensedProcessors中(记住,篡改此数据将违反软件许可规定,而且,要想通过修改注册表来使用更多的处理器,将会涉及更多的工作,而不仅仅修改这个数据)。
考虑性能 的原因,内核和HAL(在Windows 2000中,还有其他几个关键的系统文件)有单独的单处理器版本和多处理器版本。在Windows 2000上,六个系统文件(在下面的注记中将会解释)在多处理器系统和单处理器系统上分别有所不同;在32位Windows XP和Windows Server 2003系统上,只有三个系统文件有所不同(见表2.2)。在64位Windows系统上,没有PAE内核,所以,只有内核和HAL在单处理器和多处理器 系统上有所不同。
在安装的 时候,安装程序将正确的文件选择出来,并且拷贝到本地的\Windows\System32目录中。为了确定哪些文件应该被拷贝,请参见文件\ Windows\Repair\Setup.log,它列出了所有要被拷贝到本地系统磁盘的文件,以及将它们从发布介质上拷贝到哪个目录中。
表2.2 针对多处理器的系统文件和针对单处理器的系统文件
在系统磁盘上的文件名 |
在发布介质上单处理器版本的名称 |
在发布介质上多处理器版本的名称 |
Ntoskrnl.exe |
Ntoskrnl.exe |
Ntkrnlmp.exe |
Ntkrnlpa.exe(PAE内核;只有32位系统才有) |
Ntkrnlpa.exe
in \Windows\ |
Ntkrpamp.exe in \Windows \ |
Hal.dll |
取决于系统类型(参见表2.6中的HAL列表) |
取决于系统类型(参见表2.6中的HAL列表) |
下面的文件只针对 |
|
|
Win32k.sys |
\I386\UNIPROC\Win32k.sys |
\I386\Driver.cab中的 |
Ntdll.dll |
\I386\UNIPROC\Ntdll.dll |
\I386\Ntdll.dll |
Kernel32.dll |
\I386\UNIPROC\Kernel32.dll |
\I386\Kernel32.dll |
注 如 果你查一下Windows 2000发布介质文件树中的\I386\UNIPROC文件夹,你将会看到一个名为Winsrv.dll的文件。虽然此文件被放在名为UNIPROC的文 件夹的下面,好像暗示着这是一个单处理器版本,但是,实际上,该文件只有一个版本,可同时适用于多处理器和单处理器系统。在Windows XP和Windows Server 2003中,该文件夹已经被去除了。
实验:在Windows 2000上查看与多处理器相关的支持文件
你只要在设备管理器(Device Manager)中检查“计算机(Computer)”对象的驱动程序细节,就可以看到,对于一个32位Windows 2000多处理器系统,哪些文件是有所不同的。
1. 打开系统的属性对话框(你可以从控制面板中选择“系统(System)”,或者在你的桌面上右键点击“我的电脑(My Computer)”,然后选择“属性(Properties)”命令)。
2. 点击“硬件(Hardware)”标签。
3. 点击“设备管理器(Device Manager)”。
4. 展开“计算机(Computer)”对象。
5. 双击“计算机(Computer)”下面的子节点。
6. 点击“驱动程序(Driver)”标签。
7. 点击“驱动程序详细信息(Driver Details)”。
对于多处理器系统,你将会看到下面的对话框:
这 些关键的系统文件之所以有单处理器版本,是因为性能的原因——相比单处理器系统而言,多处理器的同步本质上是非常复杂和耗时的,所以,让这些关键的系统文 件有专门的单处理器版本,就可以做到在单处理器系统上避免这些同步开销(大多数运行Windows的系统都是单处理器的)。
有趣的 是,Ntoskrnl的单处理器版本和多处理器版本是通过在源代码中插入条件编译指令来生成的,而Windows 2000中的Ntdll.dll和Kernel32.dll的单处理器版本则是通过修改x86 LOCK和UNLOCK指令来创建的,这两条指令被用于同步多个线程,在单处理器版本中,利用NOP指令(什么也不做)来做同步。
Windows 的其他系统文件(包括所有的实用工具、库和设备驱动程序)都使用同样的版本,不再区分单处理器系统和多处理器系统(也就是说,它们可以正确地处理好多处理 器的同步问题)。你在你自己的软件中也应该使用这种做法,你的软件无论是一个Windows应用程序还是一个设备驱动程序——当你设计软件的时候总是记住 多处理器同步的问题,并且在单处理器系统和多处理器系统上都要做测试。
实验:检查你正在运行的Ntoskrnl的版本
在Windows 2000及其以后的版本中,不再有工具可以显示你正在运行的Ntoskrnl的版本。然而,每次当系统启动的时候,有一条日志信息记录了加载进来的内核映 像的类型——单处理器的或多处理器的,自由的(free)或者检查的(checked),如下面的屏幕截图所示。从“开始(Start)”菜单选择“程序 (Programs)/管理工具(Administrative Tools)/事件查看器(Event Viewer)”,再选择“系统(System Log)”,双击事件(Event) ID为6009的日志项,此ID表明这一项是系统启动时候写入的。
此日志项并不表明你是否引导了内核映像的PAE版本(Ntkrnlpa.exe,支持大于4GB的物理内存)。然而,你可以通过查看注册表值HKLM\SYSTEM\CurrentControlSet\Control\ SystemStartOptions来确定是否引导了PAE内核。此外,如果你引导了PAE内核,则注册表值HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Memory Management\ Physical- AddressExtension将被设置为1。
你 也可以通过检查文件属性来确定自己是否安装了多处理器版本的Ntoskrnl(或Ntkrnlpa),做法如下:运行Windows资源管理器,到\ Windows\System32文件夹下,右键点击Ntoskrnl.exe,然后选择“属性(Properties)”。然后,点击“版本 (Version)”标签,选择“源文件名(Original Filename)”属性——如果你正在运行多处理器版本的话,将会看到下面的对话框:
最后,正如前面所提到过的,你也可以在安装时通过查看文件\Windows\Repair\ Setup.log来确定到底选择了哪个内核映像和HAL。
可伸缩性
多处理器系统的一个关键问题是可伸缩性(scalability)。 为了在一个SMP系统上正确地运行,操作系统的代码必须遵守严格的指导规则。在多处理器系统中,资源竞争和其他性能问题比在单处理器系统中要复杂得多,而 且必须在系统设计的时候就要考虑清楚。Windows集下面几个特性于一身,这些特性对于Windows作为一个成功的多处理器系统起到了以下至关重要的 作用:
n 能够在任何一个可用的处理器上运行操作系统代码,也可以同时在多个处理器上运行系统代码;
n 在单个进程内执行多个线程,这些线程可以在不同的处理器上并行地执行;
n 内核内部(比如自旋锁、排队的自旋锁以及压栈锁,这些将在第3章详述)以及设备驱动程序和服务器进程内部的细粒度同步,这使得多个组件可以并行地在多个处理器上运行。
n 诸如I/O完成端口(I/O completion port,在第9章中介绍)之类的编程机制,使得可以实现高效的多线程服务器进程,并且这样的程序在多处理器系统上有很好的伸缩性。
Windows内核的伸缩性一直在不停地进步。比如,Windows Server 2003使用了针对每个CPU的调度队列,这使得可以在多个CPU上并行地执行线程调度。第6章讨论了多处理器线程调度的细节。有关多处理器同步的进一步细节可以在第3章中找到。
客户和服务器版本之间的差异
Windows 既发行客户版本的零售软件包,也发行服务器版本的零售软件包。在Windows 2000中,客户版本被称为Windows 2000 Professional;Windows 2000的3个服务器版本分别是:Windows 2000 Server、Advanced Server和Datacenter Server。
Windows XP有6个客户版本:Windows XP Home Edition、Windows XP Professional、Windows XP Starter Edition、Windows XP Tablet PC Edition、Windows XP Media Center Edition和Windows XP Embedded。后3个版本是Windows XP Professional的超集,本书并不详细地讨论这3个版本,因为它们建立在与Windows XP Professional相同的核心操作系统之上。
Windows Server 2003有6个变种:Windows Server 2003 Web Edition、Standard Edition、Small Business Server、Storage Server、Enterprise Edition和Datacenter Edition。
这些版本在以下方面有所不同:
n 所支持的处理器的数目不同;
n 所支持的物理内存的数量不同;
n 所支持的并行网络连接的数量(例如,在客户版本中,最多允许同时连接10个文件和打印服务)不同;
n Server版本随带的分层服务(layered services)并不包含在Professional版本中,例如目录服务(directory services)、集群服务(clustering),以及对多用户终端服务(multiuser Terminal Services)的支持)。
表2.3摘要列举了Windows 2000的4个版本在内存和处理器支持方面的不同。表2.4列举了Windows XP和Windows Server 2003S之间同样的信息。关于Windows Server 2003不同版本之间的详细比较,请参考 features/compareeditions.mspx。
表2.3 Windows 2000 Professional和Server几个版本之间的差异
版 本 |
支持的处理器的数目 |
支持的物理内存 |
Windows 2000 Professional |
2 |
4 GB |
Windows 2000 Server |
4 |
4 GB |
Windows 2000 Advanced Server |
8 |
8 GB |
Windows 2000 Datacenter Server |
32 |
64 GB |
表2.4 Windows XP和Windows Server 2003之间的差异
|
支持的处理器的数目(32位版本) |
支持的物理内存(32位版本) |
支持的处理器的数目(64位版本) |
支持的物理内存(Itanium版本) |
支持的物理内存(x64版本) |
Windows XP Home Edition |
1 |
4 GB |
不可用 |
不可用 |
不可用 |
Windows XP Professional |
2 |
4 GB |
2 |
不可用 |
128 GB |
Windows Server 2003 Web Edition |
2 |
2 GB |
不可用 |
不可用 |
不可用 |
Windows Server 2003 Small Business Server |
2 |
2 GB |
不可用 |
不可用 |
不可用 |
Windows Server 2003 Standard Edition |
4 |
4 GB |
不可用 |
不可用 |
不可用 |
Windows Server 2003 Enterprise Edition |
8 |
32 GB |
8 |
64 GB |
64 GB |
Windows Server 2003 Datacenter Edition |
32 |
64 GB |
64 |
1024 GB |
1024GB |
虽 然Windows操作系统有多个客户版本和服务器版本的零售软件包,但是它们共享同一组核心系统文件,包括内核映像Ntoskrnl.exe(以及PAE 版本Ntkrnlpa.exe)、HAL库、设备驱动程序,以及基本的系统辅助工具和DLL。这些文件在Windows 2000的所有版本中都是相同的。
注 Windows XP是Windows NT代码基(code base)的第一个客户版本,并且没有对应的服务器版本。不过,在Windows XP发行之后的一年多时间里,针对服务器版本的开发工作仍然在进行,这就是后来的Windows Server 2003。因此,Windows XP和Windows Server 2003中的核心系统文件是不同的。即便如此,两者的差异并不显著(实际上,有许多组件并没有改变)。
所以,如果Windows 2000 Professional和Windows 2000 Server的内核映像完全相同的话(针对Windows XP和Windows Server 2003,情况也类似),那么系统如何知道引导了哪个版本呢?只需查询注册表的HKLM\ SYSTEM\CurrentControlSet\Control\ProductOptions键下的ProductType和 ProductSuite两个值。ProductType可用来区分当前的系统是一个客户系统还是一个服务器系统,它的有效取值如表2.5所示。此结果值 存放在系统全局变量MmProductType中,在设备驱动程序中可以利用内核模式的支持函数MmIsThisAnNtAsSystem查询到此值。关于此函数,请参见Windows DDK的文档。
表2.5 ProductType注册表值
Windows版本 |
ProductType的值 |
Windows 2000 Professional、Windows XP Professional、Windows XP Home Edition |
WinNT |
Windows Server(域控制器) |
LanmanNT |
Windows Server(仅服务器) |
ServerNT |
另一个注册表值ProductSuite,既可以区别Windows服务器系统的不同版本(Stardard、Enterprise、Datacenter,等等),还可以区分Windows XP Home和Windows XP Professional系统。
如果用户程序需要判断当前运行的是哪个版本的Windows系统,他们可以调用Windows的VerifyVersionInfo函数。Platform SDK中有关于该函数的文档。设备驱动程序可以调用内核模式下的函数RtGetVersion。Windows DDK中有关于此函数的文档。
既 然系统的核心文件在客户版本和服务器版本中本质上是相同的,那么,系统在运行过程中又是如何有所不同呢?简而言之,在默认配置下,服务器系统针对系统吞吐 量做了优化,使之可以成为高性能应用服务器;而客户版本呢,虽然它也有服务器的能力,但是它针对交互式桌面用途下的响应时间做了优化。例如,在系统引导时 候,根据产品类型的不同,有几项资源分配的决策是不同的,比如操作系统堆(heap)或者池(pool)的大小、内部的系统辅助线程(system worker threads)的数量,以及系统数据高速缓存的大小。而且,运行时刻的策略——比如内存管理器用何种方式来平衡系统和进程的内存需求——在服务器版本和 客户版本中也是不同的。甚至线程调度中的有些细节在这两大产品族中也有不同的默认行为,比如时间片的默认长度,或者线程时限(quantum)(详见第6章)。凡是在两大产品族中有重要运行差异的地方,在本书接下来的有关章节中都会加以强调。除非另有特殊说明,否则,本书中介绍的内容可同时适用于客户版本和服务器版本。
检查版本
Windows 2000 Professional、Windows XP Professional和Windows Server 2003都有一个特殊的调试版本,被称为检查版本(checked build, 只有MSDN Professional或更高的订阅级别才可以获得)。这是Windows源代码重新编译得到的一个版本,编译时定义了一个称为“DBG”的标志,置了 该标志以后,用于调试和跟踪的编译时刻条件代码也被包含进来。而且,为了更加易于理解生成得到的机器代码,针对Windows二进制代码的后处理优化(为 了执行得更快而进行的代码布局结构优化)并没有被执行(参见Debugging Tools帮助文件中的“Performance-Optimized Code”部分)。
之所以提 供这样的检查版本,主要是为了帮助设备驱动开发人员,因为它可以针对那些被设备驱动程序或其他系统代码所调用的内核模式函数执行更为严格的错误检查。例 如,如果驱动程序(或者其他的内核模式代码)对一个系统函数执行了一个无效调用(比如,如果在错误的中断级别上获取一个自旋锁即spinlock),并且 该系统函数会检查传递进来的参数,那么,当问题被检测到以后系统就会停止,而不是允许某些数据结构被破环,从而导致过一段时间以后系统可能会崩溃。
实验:确认一下你是否正在运行Windows的检查版本
没有一个内置的工具可以显示出你当前正在运行的系统是检查版本还是零售版本(也称为自由版本,free build)。然而,通过WMI(Windows Management Instrumentation,Windows管理规范)的Win32_OperatingSystem类的“Debug”属性,你可以获得这一信息。下面简单的Visual Basic脚本可以显示这一属性:
strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
& "{impersonationLevel=impersonate}!\\"
& strComputer & "\root\cimv2")
Set colOperatingSystems = objWMIService.ExecQuery _
("SELECT * FROM Win32_OperatingSystem"S)
For Each objOperatingSystem in colOperatingSystems
Wscript.Echo "Caption: " & objOperatingSystem.Caption
Wscript.Echo "Debug: " & objOperatingSystem.Debug
Wscript.Echo "Version: " & objOperatingSystem.Version
Next
为了测试这段代码,请输入上述代码,并将它保存到一个文件中。下面是运行这段脚本的输出:
C:\>cscript osversion.vbs
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.
Caption: Microsoft Windows XP Professional
Debug: False
Version: 5.1.2600
该系统并没有在运行Windows的检查版本,这里显示的Debug标志为False。
在检查版 本的二进制代码中,大多数的附加代码都是使用了ASSERT宏之后的结果,该宏被定义在DDK头文件Ntddk.h中,DDK文档中有关于此宏的介绍。 ASSERT宏测试一个条件(比如一个数据结构或者参数的有效性),如果表达式的计算结果是FALSE,那么该宏调用内核模式函数RtlAssert,该函数又进一步调用DbgPrint,将调试消息的文本发送到一个调试消息缓冲区中。如果有一个内核调试器被附载到当前系统中,则该消息被自动显示在一个提示符的后面,请用户来决定该如何处理这一断言失败(断点、忽略、终止进程,或者终止线程)。
如果当前系统不是与内核调试器一起引导起来的(在 Boot.ini中使用/DEBUG开关),并且也没有附载内核调试器,那么,ASSERT测试的失败将会导致系统崩溃。关于一些内核支持例程所执行的各 种ASSERT检查,请参见Windows DDK文档中的“Checked Build ASSERTs”部分。
注 在 检查版本中,如果你比较一下Ntoskrnl.exe和Ntkrnlmp.exe,或者比较一下Ntkrnlpa.exe和Ntkrpamp.exe,你 就会发现它们是完全相同的——它们都是相同文件的多处理器版本。换句话说,Windows的检查版本并没有提供内核映像的单处理器调试版本。
检查版本对于系统管理员也是非常有用的,因为他们可以针对特定的组件跟踪到进一步的细节信息(有关详细的指令,参见Microsoft Knowledge Base中编号为314743的文章,题目为“HOWTO: Enable Verbose Debug Tracing in Various Drivers and Subsystems”)。这些信息被通过DbgPrint发送到内部的调试消息缓冲区,前面已经提到过了。为了查看这些调试消息,你或者将某个内核调试器附载到目标系统上(这要求在调试模式下引导目标系统),并且在执行本地内核调试的过程中使用dbgprint命令;或者利用来自的Dbgview.exe工具。
为了利用操作系统的调试版本,你不必安装整个检查 版本。你可以只拷贝内核映像(Ntoskrnl.exe)和正确的HAL(Hal.dll)的检查版本到一个普通的零售版本系统中。这种做法的好处是,设 备驱动程序和其他的内核代码受到了检查版本的严格检查,但是又不必运行其他所有组件的较慢的调试版本。如何做到这一点呢?详细的指令请参见Windows DDK文档中的“Installing Just the Checked Operating System and HAL”部分。因为Microsoft并没有提供Windows 2000 Server的检查版本,所以,你也可以利用这项技术在Windows 2000 Server系统上运行检查版本的内核。
最后,检 查版本对于测试那些仅仅因为系统的定时(timing)而有所不同的用户模式代码也是非常有用的(这是因为,附加的检查只是在内核中进行的,并且内核的组 件都是未经编译优化的)。通常,多线程同步错误往往跟特定的定时条件有关。如果你在检查版本的系统上(或者至少内核和HAL是检查版本的)运行测试程序, 那么由于整个系统的定时是不同的,所以这有可能使那些在普通的零售系统中不会发生的潜在定时错误暴露出来。