需求和设计目标
1989年,下面的需求驱动了Windows NT的规范:
- 提供一个真正的32位、抢占式的(preemptive)、可重入的(reentrant)虚拟内存操作系统。
- 在多种硬件体系架构和平台上运行。
- 可在对称多处理器系统(symmetric multiprocessing systems)上运行,并能很好的适应处理器的数量。
- 成为一个极好的分布式计算平台,无论是作为网络客户机还是服务器。
- 能够运行大多数已有的16位MS-DOS和Microsoft Windows 3.1应用程序。
- 符合政府对于POSIX(Portable Operating System Interface) 1003.1兼容性的要求。
- 支持Unicode,从而容易适用全球市场。
设计目标:
- 扩展性(Extensibility) 编写的系统代码必须能够随市场需求的变化而自如增长和改变。
- 可移植性(Portability) 系统必须能运行在多种硬件体系架构上,必须能根据市场的需要,相对容易地迁移到新的体系架构上。
- 可靠性(reliability)和健壮性(robustness) 系统应该能够保护自己,不会因为内部的故障和外部的篡改而不能工作。应用程序应该无法伤害操作系统或其他应用程序。
- 兼容性(compatibility)
- 性能(performance)
操作系统模型
在大多数多用户操作系统中,应用程序与操作系统本身是隔离的——操作系统内核代码运行在处理器的特权模式下(内核模式,kernelmode),可以访问系统数据和硬件;应用程序代码运行在处理器的非特权模式下(用户模式,user mode),只有很有限的一组接口可以使用,对系统数据的访问受到限制,并且无法直接访问硬件。当用户模式程序调用系统服务时,处理器执行一条特殊的指令,将调用线程切换到内核模式。当该系统服务完成时,操作系统将线程环境切换回用户模式,允许调用者继续执行。
与大多数UNIX系统类似,Windows是一个庞大而完整的操作系统;操作系统的大部分代码与设备驱动程序代码共享同样的受保护的内核模式内存空间。这意味着,操作系统的任一组件或者设备驱动程序都有可能破坏其他系统组件所使用的数据。然而,Windows实现了一-些内核保护机制,比如PatchGuard和内核模式代码签名,它们有助于减缓或避免与“共享内核模式地址空间”有关的问题发生。
操作系统的所有组件都是完全受保护的,不会被错误的应用程序破坏,因为应用程序不能直接访问操作系统中特权部分的代码和数据(不过,它们可以快捷地调用其他的内核服务)。Windows无论是作为应用服务器,还是工作站平台,从操作系统核心服务(比如虚拟内存管理、文件I/O、网络,以及文件和打印共享)的角度来看,它都很快地赢得了健壮性和稳定性两方面的良好声誉,之所以如此,对操作系统组件的这种保护是原因之一。
Windows的内核模式组件也体现了基本的面向对象设计原则。例如,它们通常并不会直接进入另一个组件的数据结构来访问该组件所维护的信息,而是使用正式的接口来传递参数,以及访问和/或修改相应的数据结构。
虽然Windows内部普遍使用了对象来表达共享的系统资源,但是,从严格意义来讲,Windows并不是一个面向对象的系统。出于移植性的考虑,Windows操作系统的大多数代码是C语言编写的。C语言并不直接支持面向对象的语法元素,比如数据类型的动态绑定,多态函数或者类继承等。
总体架构
简化的Windows架构
一条线将Windows操作系统分为了用户模式和内核模式两部分划分开来。先上面的方框代表了用户模式的进程,线下面的组件是内核模式的操作系统服务。
用户模式有如下的四种基本的类型:
- 固定的(或者硬件指定的)系统支持进程(system support process), 比如登录(logon)进程和会话管理器(session manager),它们并不是Windows服务。
- 服务进程(service process)宿纳的是Windows服务,比如task scheduler和print spooler。Windows 服务往往是要求独立于用户登录而运行。
- 用户应用程序(user application),可以是下面几种类型之一:Windows32位或者64位,Windows 3.1 16位,MS-DOS 16位或者POSIX 32位或者64位/注意,16位应用程序只能运行在32位Windows上。
- 环境子系统服务器进程(environment subsytem server process)实现了操作系统环境的支持部分。这里所谓的环境是指操作系统展示给用户或者程序员的个性部分。
上图中的子系统DLL。在Windows下,用户应用程序并不直接调用原生的Windows操作系统服务,而是通过一个或者多个子系统动态链接库(DLL)来发起调用。子系统DLL的角色是将一个已文档化的函数转化为一些恰当的内部原生系统服务调用。这一转化过程可能会——也可能不会——向正在为用户应用程序提供服务的环境子系统发送消息。
Windows的内核模式组件包含:
- Windows执行体(executive)包含了基本的操作系统服务,如内存管理、进程和线程管理、安全性、I/O、网络和跨进程通信。
- Windows内核是由一组低层次的操作系统功能构成,如线程调度(thread scheduling)、中断(interrupt)和异常分发(exception dispatching),以及多处理器同步。
- 设备驱动程序(Device driver)既包括硬件设备驱动,也包括文件系统和网络驱动程序之类的非硬件设备驱动。其中硬件设备驱动程序将用户的I/O函数调用转换成特定的硬件设备IO请求。。
- 硬件抽象层(HAL,Hardware Abstraction Layer)是指一层特殊的代码,它把内核、设备驱动程序和Windows执行体的其余部分,跟平台相关的硬件差异(比如不同主板的差异)隔离开来。
- 窗口和图形系统(windowing and graphic system)实现了用户图形界面(GUI)功能,比如对窗口的管理,用户界面控件,以及绘制。
文件名称 | 组件 |
---|---|
Ntodkrnl.exe | 执行体和内核 |
Ntkrnlpa.exe(仅用于32位系统) | 执行体和内核,支持物理地址扩展(PAE),使得32位系统可寻址多达64GB物理内存,以及将内存标记为不可执行的 |
Hal.dll | 硬件抽象层 |
win32k.sys | Windows子系统的内核模式部分 |
ntdll.dll | 内部支持函数,以及执行体函数的系统服务分发存根(stub) |
kernel32.dll,Advapi32.dll user32.dll,gdi32.dll | Windows的核心子系统DLL |
可移植性
Windows主要通过以下两种方法来实现可移植性,以支持多种硬件体系架构和平台:
Windows有一个分层设计,系统的低层部分是与处理器体系架构相关的,或者是与平台相关的,它们被隔离到独立的模块中,所以,系统的高层部分可以不考虑体系架构之间的差别,也不用关心硬件平台的差异。有两个关键的组件为操作系统提供了可移植性,它们是内核(包含在Ntoskrnl.exe中)和硬件抽象层( 或称HAL,包含在Haldll中)。与体系架构相关的功能,比如线程环境切换(thread context switching)和陷阱分发(trap dispatching)是在内核中实现的。在同样的体系架构下,不同系统之间有所差异的功能(例如,不同的主板)则是在HAL中实现的。另外还有唯一一个组件也有相当数量的代码是与体系架构相关的,那就是内存管理器,但是与整个系统相比,这仍然只是一小部分而已。.
Windows的绝大部分代码是用C语言编写的,少部分是用C++编写的。只有那些需要直接与系统硬件打交道的部分(比如中断陷阱处理器(interrupt trap handler)),或者对性能极端敏感的操作系统部分( 比如环境切换(context switching)),才是用汇编语言编写的。汇编语言代码不仅出现在内核和HAL中,也出现在操作系统核心的其他一些地方 (比如实现了互锁指令的例程,以及本地过程调用设施中的模块),还出现在Windows子系统的内核模式部分,甚至在某些用户模式库中,比如Ntdll.dll中的进程启动代码。
对称多处理
多任务(multitasking)是指让多个执行线程共享同一个处理器的操作系统技术。当一台计算机有不止一个处理器时,它可以执行多个线程。
Windows是一个对称多处理器(SMP,symmetric multiprocessing)操作系统。没有主处理器——操作系统和用户线程可以被调度到任何处理器上运行。而且,所有处理器共享唯一的内存空间。
在非对称多处理器(ASMP,asymmetric multiprocessing)操作系统中,系统选择其中一个处理起来执行操作系统内核代码,而其他的处理器只能运行用户代码。
对称和非对称多处理器
Windows也是支持三种现代的多处理器系统:多核、超线程和NUMA(非一致的内存架构,non-uniform memory architecture)。
超线程(Hyper-Threading)是Intel引入的一项技术,它可以在每个物理核上提供多个逻辑处理器。每个逻辑处理器有自己的CPU状态,但是执行引擎和片上缓存则是共享的。这使得一个逻辑CPU可以在其他逻辑CPU停转(比如缓存未命中,或者分支预测错误)的时候继续执行。调度算法已经被改进过了,因而可以最佳地利用支持超线程的机器,例如,原来的做法是,将线程调度到一个空闲的物理处理器上,现在则改进为“选择一个物理处理器上的空闲逻辑处理器(该处理器的其他逻辑处理器可能正忙着)”。
在NUMA系统中,处理器被组织成更小的单元,称为节点(node)。每个节点有它自己的处理器和内存,并且通过一个缓存一致(cache-coherent) 的互连总线连接到更大的系统上。NUMA系统上的Windows仍然作为一个SMP系统来运行,其中所有的处理器可以访问所有的内存——只不过,节点本地的内存访问起来比其他节点的内存更快一些而已。系统提高性能的做法是,根据线程用到的内存所在的节点,将线程调度到同一节点中的处理器上。系统尽可能地在节点内部满足内存申请的请求,只有在必要的时候才从其他的节点分配内存。
多核系统一一因为这些系统有多个真正的物理核(只是在同一个芯片上),Windows中原来的SMP代码将这些核看作单独的处理器,但是,一些特定的需要记录和标识核的任务(比如许可管理)例外,这些任务需要区分是同一个处理器的核,还是不同插槽上的核。
Windows最初设计的时候并没有特定的处理器个数限制,只是不同的许可策略使Windows的不同发行版本有了差异。然而,为了方便和高效起见,Windows用一个位掩码(有时候称为亲和性掩码(affinity mask))来记录和跟踪处理器(总数、空闲、忙,或诸如此类的细节),这里位数与机器的原生数据类型(32位或64位)相同,因而使得处理器可以直接在一个寄存器里操纵这些位。由于这个原因,Windows系统最初限定了CPU的个数在一个原生字的范围内,因为亲和性掩码不可能任意增长。
为了保持兼容性,以及支持具有更多处理器的系统,Windows实现了一个更高级的概念,称为处理器组(processor group)。处理器组是指可以由一个亲和性掩码来定义的一组处理器,内核和应用程序在更改亲和性设置的过程中可以选择它们将使用哪一组。应用程序为保持兼容,可以查询当前系统支持多少组(目前限制4组),然后枚举出每个组的位掩码。同时,老的遗留下来的应用程序继续工作,它们只会看到它们当前所在的组。
如前所述,实际许可支持的处理器数目取决于所用的Windows发行版本(参见后面的表2.2)。该数值作为策略值“Kermel-RegisteredProcessors”存放在系统许可策略文件中(\Windows\ServiceProfiles\NetworkService\AppData\Roaming\Microsoft\SofwareProtectionPlatform\tokens.dat)。(记住,篡改此数据将违反软件许可规定,而且,通过修改许可策略来使用更多的处理器,涉及更多的工作,不仅仅是修改这个值。)
可伸缩性
为了在一个SMP系统上正确的运行,操作系统的代码必须遵守严格的知识和规则。在多处理器系统中,资源竞争和其他性能问题比在单处理器上要复杂得多,而且必须在设计的时候就考虑清楚。Windows的下面几个特性,对多处理器的成功起到至关重要的作用:
- 能够在任何可用处理器上运行操作系统代码,也可以同时在多个处理器上运行系统代码。
- 在单个进程内执行多个线程,每个线程可以在不同的处理器上并行地执行。
- 内核内部的细粒度同步(自旋锁、排队的自旋锁、以及堆锁),以及在设备驱动程序和服务器进程内部的细粒度同步,这使得更多的组件可以在多个处理器上并发执行。
- 诸如完成端口之类的变成机制,是的可以实现高效的多线程服务器进程,并且这样的进程在多处理器系统上有很好的伸缩性。
客户机和服务器版本之间的差异
windows7 客户机版本:
Windows 7 Home Basic、Windows 7 Home Preminum、Windows 7 Professional、Windows 7 Ultimate、Windows 7 Enterprise和Windows 7 Starter。
这么多的版本,系统如何知道引导的发行版本是哪个呢?
只需查询注册表 计算机\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\ProductOptions 键下的ProductType和ProductSuite两个值。ProductType可用来区分当前系统是一个客户机系统还是服务器系统。
windows版本 | ProductType |
---|---|
Windows 客户机 | WinNT |
Windows 服务器(域控制器) | Lanman NT |
Windows 服务器(仅服务器) | ServerNT |
ProductSuite,包含了tokens.dat文件数据中的一份缓存副本,它随Windows版本的不同而不同,而且也随系统支持的特性的不同而不同。
如果需要判断当前运行的是哪个版本的Windows系统,可以调用Windows的VerifyVersionInfo函数。Windows SDK中有关于该函数的文档。设备驱动程序可以调用内核模式函数RtVerifyVersionInfo。WDK中有关于此函数的文档。
关键的系统组件
如下图所示,显示了大部分操作系统组件。
环境子系统和子系统DLL
环境子系统的作用是将基本的Windows执行体系统服务的某个子集暴露给应用程序。每个子系统都提供了对于Windows原生服务的一个不同子集的访问能力。这意味着,建立在某个子系统的应用程序可以做到的事情,可能时另一个建立在不同子系统上的应用所无法做到的。例如,Windows应用程序不能够使用SUA的fork函数。
映像文件运行时,负责创建进程的代码会检查该映像头部的子系统代码类型,使它可以通知正确的子系统,有新的进程被创建
用户程序并不直接调用Windows系统服务,而是通过一个或者多个子系统dll来进行。
当应用程序调用子系统dll中的某个函数时,可能会发生下面三件事情之一:
- 该函数完全是在子系统dll中实现的,在用户模式下运行。也就是说,该函数没有给环境子系统进行发消息,也没有调用Windows执行体系统服务。该函数实在用户模式下完成的,运行的结果被返回给调用者。此类函数的例子有GetCurrentProcess(他总是返回-1,在所有有关进程的函数中,-1被定义为代表当前进程)和GetCurrentProcessId(对于一个正在运行的进程,进程ID不会变,所以此进程ID可以从某个缓存的地方获取到,避免了调用内核)。
- 该函数要求调用Windows执行体多次或一次。例如,Windows的ReadFile和WriteFile函数分别要调用底层内部windows IO系统服务NtReadFile和NtWriteFile。
- 该函数要求在环境子系统进程中完成某些工作(环境子系统进程运行在用户模式下,负责维护那些在其控制下运行的客户应用程序的状态)。该函数通过消息的形式向环境子系统发送客户机/服务器请求,从而让子系统执行某个操作。然后子系统dll等待应答,收到应答后再返回调用者。
子系统启动
有会话管理器(Session Manager)(Smss.exe)进程启动起来,子系统的启动信息保存在注册表键\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems下面。
Required值列出了系统引导时加载的子系统。该值有两个字符串:Windows和Debug。Windows值包含了Windows子系统的文件规范,Csrss.exe,它代表了客户机/服务器运行时子系统。Kmode值包含了Windows子系统的内核模式部分的文件名称,win32k.sys。
Windows子系统
Windows子系统提供基本功能:处理窗口和显示IO,让其它子系统调用Windows子系统完成上述功能。
这种设计的结果,对于Windows系统,Windows子系统是一个必须的组件,即使在一个没有交互用户登录的服务器上也是如此。因此,Windows子系统进程被标记为关键进程。
其主要有以下组件构成:
- 对于每个会话,环境子系统进程(Csrss.exe)有一个实例加载三个DLL(Basesrv.dll、Winsrv.dll和Csrsrv.dl)
- 内核模式设备驱动程序(win32k.sys)
- 控制台宿主进程(conhost.exe),提供对控制台应用程序的支持。
- 子系统dll,将已文档化的Windows API函数,转译成Ntoskrnl.exe和win32k.sys中恰当的且绝大多数未文档化的内核模式系统服务调用。
- 图形设备驱动程序,与硬件相关的图形显示器驱动程序、打印机驱动程序和视频微端口驱动程序。
因为子系统大部分运行在内核模式下,所以只有少数一些Windows函数要向Windows子系统进程发送消息:进程和线程的创建和终止、网络驱动器号,以及临时文件的创建。一般而言,一个正在运行的Windows应用程序不会引起很多至Windows子系统进程的环境切换。
控制台窗口宿主进程
Windows为系统中的每个控制台窗口使用了一个单独的进程:控制台窗口宿主进程(Conhost.exe)。(多个控制台程序可以共享一个控制台窗口,比如在从命令提示符窗口发起一个新的命令提示符窗口时。默认情况下,第二个命令提示符共享前者的控制台窗口。)
只要控制台程序向当前会话中正在运行的Csrss实力注册其自身,Csrss利用客户进程的安全令牌,而非Csrss的System令牌来创建Conhost实例。然后,它映射一个共享内存区,让所有的Conhost都可以与Csrss共享一部分内存,以便于高效地处理缓冲区。
Ntdll.dll
Ntdl.dIl是一个特殊的系统支持库,主要用于子系统DLL。它包含两种类型的函数:
- 系统服务分发存根( stub),它们会调用Windows执行体的系统服务。
- 内部支持函数,供子系统、子系统DLL以及其他的原生映像文件使用。
第一组函数为Windows执行体系统服务提供了接口,在用户模式下可以通过这些接口函数调用Windows执行体的系统服务。这样的函数超过了400个,比如NtCreateFile、NtSetEvent等。如前所述,这些函数的大多数功能可以通过WindowsAPI来访问得到(然而,有些函数则不然,它们仅被用于操作系统内部)。
对于每一个这样的函数,Ntdll包含 了一个同名的入口点。函数内部的代码包含了与处理器体系架构相关的模式切换指令,通过该指令可转换到内核模式下,从而调用系统服务分发器(system service dispatcher)。系统服务分发器在检验某些参数以后,再调用真正的内核模式系统服务,其中包括Ntoskml.exe内部的实际代码。
Ntdll也包含许多支持函数,比如映像加载器(以Ldr开头的函数)、堆管理器、Windows子系统进程通信函数(以Csr开头的函数)。Ntdll也包含一般性的运行库例程(以RtI开头的函数)、对用户模式调试和Windows事件跟踪的支持函数(分别以DbgUi和Etw开头的函数),以及用户模式异步过程调用(APC, Asynchronous ProcedureCall)分发器和异常分发器(关于APC和异常)。最后,在NtdlI中存在一个很小的C运行库(CRT)例程的子集,仅限于字符串和标准库中的一些例程, 比如memcpy, strcpy、 itoa,等等。
执行体
Windows执行体是Ntoskrnl.exe中的上层(内核是其下层)。执行体包含以下类型的函数:
- 可在用户模式下调用的导出函数。这些函数称为系统服务(system service),并且通过Ntdll被导出。这些服务绝大多数可通过Windows API来访问,或者通过另一个环境子系统的API来访问。然而对于有些服务,通过任何一个文档化的子系统函数都无法访问(这样的例子有ALPC、诸如NtQueryInformationProcess之类的各种查询函数,以及诸如NiCreatePagingFile这样的专用函数,等等)。
- 可通过DeviceloControl函数来调用的设备驱动程序函数。这为从用户模式到内核模式提供了一个通用的接口,因而在用户模式下可以调用设备驱动程序中并不与读或者写操作关联的函数。
- 只能在内核模式下调用的导出函数,这些函数在WDK中已经文档化。
- 在内核模式下调用,但未在WDK中文档化的导出函数(比如以Inbv开头的、在引导视频驱动程序中调用的函数)
- 定义为全局符号但是未被导出的函数。这包括在Ntoskrnl内部调用的支持函数,比如以op或者Mi开头的那些函数(分别是I/O管理器内部支持函数和内存管理内部支持函数)。
- 未定义为全局符号,而是在一个模块内部的函数。
执行体包含了以下的主要组件:
- 配置管理器(configurationmanager)负责实现和管理系统的注册表。
- (process manager)创建和终止进程和线程。针对进程和线程的底层支持是在Windows内核中实现的;而执行体则在这些低层对象的基础上又加上了额外的语义和功能。
- 进程管理器安全引用监视器(security reference manager, SRM)强制在本地计算机上实行安全策略。它守护着操作系统的资源,执行对运行时对象的保护和审计。
- I/O管理器 (IO manager)实现了与设备无关的I/O功能,负责将I/O请求分派到恰当的设备驱动程序以进一步处理。
- 即插即用(PnP)管理器(Plug and Play manager)的任务是,为了支持某个特定的设备,确定哪些驱动程序是必需的,同时也负责加载这些驱动程序。它在设备枚举过程中,获取到每个设备的硬件资源需求。PnP管理器根据每个设备的资源需求,分配适当的硬件资源,比如I/O端口、IRQ、 DMA通道和内存位置。当系统中的设备变化(增加或者移除设备)时,它还负责发送恰当的事件通知。
- 电源管理器 (power manager)负责协调电源事件,并且产生电源管理I/O通知,发送给设备驱动程序。电源管理器可以配置成:当系统空闲时,通过将CPU置于睡眠状态而降低电源消耗。单个设备的电源消耗变化可由设备驱动程序来处理,但是需要电源管理器来协调。
- Windows驱动程序模型( Windows Driver Model)的WMI例程允许设备驱动程序发布有关性能和配置的信息,以及接收来自用户模式WMI服务的命令。WMI信息的消费者可以运行在本地机器上,也可以在跨网络的远程机器上。
- 缓存管理器 (cache manager)提高了基于文件的IO操作的性能,其做法是,让最近引用过的磁盘数据驻留在主内存中以便快速访问(并且延迟磁盘写操作,在将更新数据发送到磁盘之前先在内存中停留一小段时间)。你将会看到,它利用了内存管理器对映射文件的支持来做到这一点。
- 内存管理器( memory manager)实现了虚拟内存。这是一种内存管理方案,它为每个进程提供一个巨大的私有地址空间,其大小可以超过当前可用的物理内存。内存管理器也为缓存管理器提供相应的底层支持。
- 逻辑预取器(logical prefetcher)和Superfetch 用于加速系统和进程的启动过程。其做法是,对于要在系统或进程启动过程中引用的数据,它优化这些数据的加载过程。
另外,Windows执行体还包含四组主要的支持函数,以上这些执行体组件会用到这些支持函数。在这些支持函数中,差不多三分之一在WDK中有相应的文档,因为设备驱动程序也要用到它们。以下就是这四大类支持函数。
对象管理器(object manager),创建、管理和删除Windows执行体对象和抽象数据类型,它们代表了操作系统的资源,比如进程、线程和各种同步对象。
高级LPC设施 (ALPC facility),为同一台机器上的客户机进程和服务器进程传递消息。此外,ALPC也被用作RPC的一个本地传输实现,这里的RPC是指跨网络的客户机进程和服务器进程之间的工业标准通信设施。
一组涉及范围广泛的公共运行库函数,比如字符串处理、算术操作、数据类型转换,以及安全结构处理等。
执行体支持例程, 比如系统内存分配(换页的和非换页的内存池)、互锁的内存访问,以及三种特殊类型的同步对象:资源、快速互斥体( fast mutex)和推锁( pushlock)。
执行体中还包含了其他多种基础设施历程
- 内核调试器库(kerneldebuggerlibrary)使得内核的调试与支持KD的调试器保持独立,这里KD是指一个可移植的协议,可以承载在各种传输体(比如USB和IEEE1394)上。WinDbg和Kd.exe工具实现了KD协议。
- 用户模式调试框架 (user-mode debuging framework)负责向用户模式调试API发送事件,支持断点和单步跟踪代码,以及改变运行线程的执行环境。
- 内核事务管理器( kernel transaction manager)提供公共的两阶段提交机制供资源管理器(resource manager) 使用,比如事务型注册表(TxR,transactional registry) 和事务型NTFS (TxF,transactional NTFS)。
- 超级管理器库(hypervisor library)是Windows Server 2008中Hyper-V栈的一部分, 它提供了虚拟机环境的内核支持。当系统知道它在一个客户区(虚拟环境)中运行时,超级管理器库可以优化相应的内核代码。
- 错误修正管理器(errata manager)为非标准的或非兼容的硬件设备提供绕行的解决方案。
- 驱动程序检验器( Driver Verifer)为内核模式驱动程序和代码提供可选的一致性检查机制。
- Windows事件跟踪( Event Tracing for Windows)为内核模式和用户模式组件提供了许多用于在系统范围内进行事件跟踪的辅助例程。
- Windows诊断设施 ( Windows diagnostic infrastructure),对基于诊断场景(diagnostic scenario)的系统活动进行智能跟踪。
- Windows硬件错误体系架构( Windows hardware error infrastructure)支持例程提供了一个用于报告硬件错误的公共框架。
- 文件系统运行库( file-system runtime library)为文件系统驱动程序提供了一组公共的支持例程。
内核
内核是由Ntoskrnl.exe的一组函数度与硬件体系架构的低层支持构成的。
Ntoskrnl.exe中的这组函数提供了一些最为基本的机制,如线程调度和同步服务;而硬件的低层支持则随处理器架构的不同而有所区别。内核代码主要是用c编写的,对于那些要用到的特殊处理器指令和寄存器的任务,则保留使用汇编代码的形式。
内核对象
内核提供了一组定义明确的、可预知的操作系统低层原语和机制,从而使得执行体中的高层组件可以做它们需要做的事情。内核实现了操作系统的基本机制,并且避免各种策略决定,从而将自己与执行体的其余部分分离开。它几乎将所有的策略决定都留给了执行体,唯一的例外是线程调度和分发,这是由内核自己来实现的。
从内核外部来看,执行体将线程和其他可共享的资源都表示为对象。这些对象需要一些策略开销,比如用以维护它们的对象句柄( object handle),以及用以保护它们的各种安全检查,还有相应的资源配额(当它们被创建时资源配额就会被扣除)。这些开销在内核中是不存在的,内核实现了一组更为简单的对象,称为内核对象(kernel object),它们帮助内核控制好中心处理过程,并且支持执行体对象的创建工作。绝大多数执行体层的对象都封装了一个或者多个内核对象,把它们的内核属性合并在一起。
一组称为控制对象( control object)的内核对象建立了有关控制各种操作系统功能的语义。这包括APC对象、DPC (Deferred Procedure Call,延迟过程调用)对象,以及I/O管理器使用的一些对象,比如中断对象等。
另一组称为分发器对象( dispatcher object)的内核对象融合了同步的能力,可以改变或者影响线程的调度。分发器对象包括内核线程、互斥体(内部称为突变体即mutant)、事件、内核事件对(event pair)、信号量(semaphore)、定时器,以及可等待的定时器(waitable timer)。执行体利用内核函数来创建和维护内核对象实例,并且构建更加复杂的、提供给用户模式的对象。
内核处理器控制区和控制块(KPCR和KPRCB)
内核使用一个称为处理器控制区( processor control region, KPCR) 的数据结构来存放与处理器有关的数据。KPCR包含了基本的信息,例如处理器的中断分发表(IDT)、任务状态段(TSS)和全局描述符表(GDT)。它也包括中断控制器的状态,这是内核与其他模块(比如ACPI驱动程序和HAL)共享的数据。为了便于访问KPCR,在32位Windows上, 内核在fs寄存器中保存了一个指向KPCR的指针;在x64 Windows系统上,指向KPCR的指针存放在gs寄存器中。在IA64系统上,KPCR总是位于0000000fff0000。
KPCR也包含一个称为内核处理器控制块( kernelprocessor control block, KPRCB)的内嵌数据结构。KPCR是已经文档化的数据结构,因而第三方的驱动程序和其他的Windows内核组件可以使用:与此不同的是,KPRCB是-一个私有的数据结构,仅仅Ntoskrnl.exe中的内核代码使用该结构。KPRCB包含了调度信息(比如在该处理器上正在调度的当前线程、下一个执行的线程以及空闲线程)、该处理器的分发器数据库(其中包含了每个优先级的就绪队列)、DPC队列、CPU厂商和标识符信息(型号(model)、 步进(stepping)、 速度、特征位)、CPU和NUMA拓扑(节点信息、每个芯片的核、每个核的逻辑处理器,等等)、 缓存大小、时间计数信息(比如DPC和中断时间),等等。KPRCB还包含了所有关于该处理器的统计信息,比如I/O统计、缓存管理器的统计(相关描述参见本书下册第11章“缓存管理器”)、DPC统计,以及内存管理器的统计。最后,KPRCB有时候也被用来存储一些缓存对齐的、针对每个处理器的数据结构,以便于优化内存访问,尤其是在NUMA系统上。例如,系统中非换页的和换页的内存池快查表也存储在KPRCB中。
如下所示,我们可以在内核调试时观察上述结构:
1 | 0: kd> !pcr |
硬件支持
内核的另一个主要任务是将执行体和设备驱动程序从Windows所支持的各种硬件体系架构的差异中抽象或隔离出来。这项任务包括处理各种功能(比如中断处理、异常分发和多处理器同步)方面的变化情况。
即便是对这些与硬件相关的功能,在设计内核时也力图使公共代码尽可能最大化。内核支持一组可移植的接口,这组接口的语义在不同的体系架构上是等同的。而且,实现这组可移植接口的大部分代码在不同的体系架构上也是等同的。
内核中有一小部分代码涉及与x86有关的接口,之所以需要这部分代码,是为了支持老的MS-DOS程序。这些x86接口并不是可移植的,因为在任何其他体系架构的机器上它们都不可能被调用:而且它们根本不会出现在这样的机器上。例如,与x86相关的代码提供了相应的功能调用,可用来维护全局描述符表(GDT)和局部描述符表(LDT),这正是x86的硬件特性。
在内核中与体系架构相关的代码的另-一个例子是,提供“转译缓冲区( translation buffer)”和“CPU缓存”支持的接口。为了提供这样的支持,不同的体系架构需要不同的代码,因为处理器缓存的实现方式各有不同。
另一个例子是环境切换。尽管从高层来看,线程选择和环境切换可以使用同样的算法(上一个线程的执行环境被保存起来,新线程的环境被加载进来,然后新线程被启动执行),但在不同的处理器上,具体的实现还是存在体系架构方面的差异。因为执行环境是由处理器的状态(寄存器等)来描述的,所以哪些信息应该被保存或加载,随体系架构的不同而有所不同。
硬件抽象层(HAL)
Windows设计的关键要素之一是,它能被移植到各种不同的硬件平台上。硬件抽象层(HAL)是使得这种可移植性成为可能的一个关键部分。HAL是一个可加载的内核模式模块(Hal.dIl),它提供了针对Windows当前运行所在的硬件平台的低层接口。它隐藏了与硬件相关的细节,比如I/O接口、中断控制器,以及多处理器通信机制——任何与体系架构相关或者与机器相关的功能。
所以,Windows内部组件以及用户编写的设备驱动程序并不直接访问硬件;而是当需要获得与平台相关的信息时,通过调用HAL例程来保持可移植性。出于这一原因,这些HAL例程在WDK中也被文档化了。更多有关HAL及其在设备驱动程序中用法的信息,参见WDK。
虽然Windows附带了几个HAL(如下),但是它有能力在引导时检测到应该使用哪个HAL,因而,在早期Windows版本上“试图在不同类型的系统上引导已安装的Windows系统”的问题便不复存在。
HAL文件名 | 所支持的系统 |
---|---|
Halacpi.dll | 高级配置和电源接口(ACPI)PC。隐含只能是单处理器机器,并且无APIC支持 |
Halmacpi.dll | 支持ACPI的APIC(高级可编程中断控制器)PC.一旦使用APIC,则意味着支持SMP |
1 | 注 在x64机器上只有一个HAL映像文件,HAL.dll。由于其机器具有相同的主板配置,x64处理器要求同时支持ACIP和APIC |
设备驱动程序
设备驱动程序是可加载的内核模式模块,在IO管理器和相应的硬件之间建立起连接。设备驱动程序运行在内核模式下,位于以下三种执行环境之一:
- 在发起IO功能的用户线程环境中
- 在内核模式系统线程的环境中
- 作为中断的结果
驱动程序往往是C来编写的,因此,通过正确的使用HAL历程,驱动程序可以再Windows所支持的CPU体系架构进行源代码级的移植,而在同一个体系架构族内则是二进制可移植的。
设备驱动类型:
- 硬件设备驱动(hardware device driver)通过HAL 操纵硬件,从而将输出写入到物理设备或网络中,或者从物理设备或网络上接收输入。硬件设备驱动程序也有许多类型,比如说总线驱动程序、人机界面驱动程序、大容量存储设备驱动程序。
- 文件系统驱动程序(file system driver)是指:可以接受面向文件的IO请求,并且将这些请求转换成针对某一特定设备的IO请求。
- 文件系统过滤驱动程序(file system filter driver),如执行磁盘镜像加密的驱动程序,或者截取IO请求并执行某些增值处理之后再把IO传递给下一层的驱动程序。
- 网络重定向器(network redirector)和服务器,分别指那些将文件系统IO请求传递给网络上某台机器,或者从网络上接收此类请求的文件驱动程序。
- 协议驱动程序(protocol driver),实现如TCP/IP、NetBEUI和IPX/SPX之类的网络协议。
- 内核流式过滤驱动程序(kernel streaming filter driver),这样的驱动程序北川接起来对数据流进行信号处理,比如录制或者播放视频或者音频。
Windows驱动程序基础(WDF)
Windows驱动程序基础( WDF,Windows Driver Foundation)简化了Windows驱动程序的开发,它提供两个框架:内核模式驱动程序框架( KMDF,Kernel-Mode Driver Framework)和用户模式驱动程序框架(UMDF,User Mode Driver Framework)。开发人员可以用KMDF来为Windows 2000 SP4及以后的系统编写驱动程序,而UMDF仅支持Windows XP及以后的系统。
KMDF提供了一个简单的WDM接口,向驱动程序开发人员隐藏了WDM的复杂性,开发人员无须修改底层的总线/功能/过滤驱动程序模型。KMDF驱动程序响应它们登记过的事件,并调用KMDF库来完成那些并不特定于它们所管理硬件的工作,比如一般性的电源管理或同步(以前,每个驱动程序必须要实现它自己的一份管理工作)。在有些情况下,200多行的WDM代码可以用一个简单的KMDF函数调用来替代。
UMDF使得某些特定类型的驱动程序(主要是基于USB或其他高延迟协议的总线)可以实现为用户模式的驱动程序,比如摄像机、MP3播放器、移动电话、PDA以及打印机的驱动程序。UMDF运行每个用户模式驱动程序,本质上这是-一个用户模式服务,它使用ALPC与真正访问.硬件的内核模式包装驱动程序进行通信。如果–个UMDF驱动程序崩溃了,它的进程将死掉,通常还会重新启动,所以系统不会招致不稳定一—仅仅在宿纳该驱动程序的服务进程重新启动的过程中该设备不可用。最后,UMDF驱动程序是用C++按照COM风格的类和语义来编写的,可以进一步降低程序员编写设备驱动程序的门槛。
下表列出了执行体组件通常会用到的大多数名称前缀。
前缀 | 组件 |
---|---|
Cc | 公共缓存(Common Cache) |
Cm | 配置管理器 |
Dbgk | 用户模式的调试框架 |
Em | 错误修正管理器 |
Etw | Windows事件跟踪 |
Ex | 执行体支持例程 |
FsRtl | 文件系统驱动程序运行库 |
Hvl | 超级管理器库 |
Io | IO管理器 |
Kd | 内核调试器 |
Ke | 内核 |
Lsa | 本地安全权威 |
Mm | 内存管理器 |
Nt | NT系统服务(绝大多数被导出为Windows函数) |
Ob | 对象管理器 |
Pf | 预取器 |
Po | 电源管理器 |
Pp | Pnp管理器 |
Ps | 进程支持 |
Rtl | 运行库 |
Se | 安全性 |
Sm | 存储管理器(Store Manager) |
Tm | 事务(Transaction Manager) |
Vf | 检验器(Verifier) |
Wdi | Windows诊断设施(Windows Diagnostic Infrastructure) |
Whea | Windows硬件错误体系架构(Windows hardware error architecture) |
Wmi | Windows 管理设施 |
Zw | 以Nt开头的系统服务入口点的镜像,它把原先的访问模式设置为内核模式,从而消除了参数的有效性检查过程,因为Nt系统服务只要有当原来的访问模式为用户模式时才进行参数有效性检查。 |
一般来说,函数一般的格式如下:
<前缀><操作><对象>
例如,ExAllocatePoolWithTag识一个负责从换页或者非换页的内存池中进行内存分配的执行体支持例程。
系统进程
以下系统进程会出现在每一个Windows字体中:
- Idle进程(他为每个CPU包含一个对应的线程,占用空闲的CPU事件)。
- System进程(包含大多数内核模式进程线程)。
- 会话管理器(Smss.exe)
- Windows子系统(Csrss.exe)
- 会话0初始化(Wininit.exe)
- 登录进程(Winlogon.exe)
- 服务控制管理器(Services.exe)和它创建的子服务进程
- 本地安全认证服务器(Lsass.exe)
系统空闲进程
上图列出的第一个进程是系统空闲进程(Idel),此进程没有一个实际的用户模式映像文件,因此其名称在不同的工具下也不尽相同。:
实用工具 | 进程ID为0的进程名称 |
---|---|
任务管理器 | System Idle Process |
Process Status | Idle Process |
Process Explorer(Procexp.exe) | System Idle Process |
Task List(Tasklist.exe) | System Idle Process |
Tlist(Tlist.exe) | System Process |
System进程和系统线程
System进程(ID为4)是某种特殊线程的母体,这种特殊的线程只能在内核模式下运行,称为内核模式系统线程(Kernel-mode System Thread)。系统线程具备普通用户模式线程所有的属性和环境,但是不同的地方在于,他们只在内核模式下运行系统空间中加载的代码,无论这些代码是在Ntoskrnl.exe还是在其他加载进来的设备驱动程序中。而且,系统线程没有用户进程地址空间,因此,任何的动态存储需求,都必须从操作系统的内存堆中分配,比如从一个换页或者非换页的内存池中非配。
系统线程是通过PsCreateSystemThread函数(WDK中有文档说明)来创建的,该函数只能从内核模式中调用。Windows以及各种设备驱动程序在系统初始化阶段创建系统线程,以便执行各种要求线程环境的操作,比如发出和等待I/O或其他对象,或者查询(poll) 一个设备。例如,内存管理器使用系统线程来实现诸如“将脏页面写到页面文件或者映射文件中”、“将进程在内存中换进/换出”之类的功能。内核会创建一个称为平衡集管理器( balance set manager)的系统线程,它每秒钟被唤醒一次,从而有可能发出各种与调度和内存管理相关的事件。缓存管理器也使用系统线程来实现“预读(read-ahead)” 和“滞后写(write-behind)” 的I/O。文件服务器设备驱动程序(Srv2.sys) 利用系统线程来响应那些“针对已共享磁盘分区上的文件数据”的网络I/O请求。甚至软盘驱动程序也有-一个系统线程来查询软盘设备(在这种情况下,定期查询更加有效,因为一个靠中断驱动的软盘驱动程序要消耗大量的系统资源)。有关特定系统线程的进一步信息, 见相应组件的章节。
在默认情况下,系统线程是属于System进程的,但是,设备驱动程序可以在任何一个进程中创建系统线程。例如,Windows子 系统设备驱动程序( Win32k.sys)在规范的显示驱动程序(Canonical Display Driver, Cdd.dIl) 中创建一个 系统线程,这里Cdd.dll是Windows子系统进程(Csrss.exe)的一”部分,因而新线程可以很容易地访问该进程用户模式地址空间中的数据。
在诊断或者进行系统分析时,若能够将一个系统线程的执行过程映射回驱动程序上,甚至映射到包含该代码的子例程,一定非常有用。例如,在一个负载很重的文件服务器上,System进程很有可能会消耗掉相当可观的CPU时间。但是,仅仅知道“System进程运行时‘某个系统线程’正在运行”,是不足以确定哪个设备驱动程序或者操作系统组件正在运行的。
所以system进程中的线程正在运行,则首先需要确定那些线程正在运行。一旦找到了这些现车,可以看一下改系统线程是在哪个驱动程序中开始执行的(可以知道可能是哪个驱动程序创建了这个线程),或者检查一下所涉及的线程的调用栈,通过调用栈可以 知道该线程当前在哪里执行。
会话管理器
会话管理器(%SystemRoot%\System32\Smss.exe)是系统中创建的第一个用户模式进程。这一进程由负责完成执行体和内核初始化工作最后阶段的内核模式系统线程创建。
Smss启动时,会检查自己是第一个实例(主Smss),还是主Smss为了创建会话而启动起来的一个实例。(如果存在命令行参数,则是后一种情形。) 通过在引导过程中以及在终端服务会话的创建过程中创建多个Smss实例,Smss可以同时创建多个会话(最多4个并发会话,再为除去一个CPU外每个额外的CPU加上一个额外的会话)。这一能力增强了终端服务器系统的登录性能,在这种终端服务器系统上,同时会有许多个用户连接上来。一旦一个会话完成了初始化,该会话的Smss副本便终止。因而结果是,只有初始的Smss .exe进程仍然是活动的。(关于终端服务的描述,参见第1章的“终端服务及多个会话”一节。)
主Smss执行下面的一次性初始化步骤:
1.将该进程和初始线程标记为“关键的(critical)”。 (如果一个被标记为“关键的”的进程或线程退出,则Windows崩溃。)
2.将进程的基本优先级提升到11。
3.如果系统支持动态增加热处理器,则允许自动更新处理器亲和性,这样,如果新的处理器被加入进来,新的会话将可以利用这些新加入的处理器。
4.创建相应的命名管道和邮件槽,用于Smss、Csrss、Lsm 之间的通信。
5.创建ALPC端口接受命令。
6.根据\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Environment中的定义,创建系统范围的环境变量。
7.根据\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\DOS Devices中的定义,在对象管理器名字空间的\Global??目录下为该注册表键中定义的设备创建符号链接。
8.在对象管理器名字空间中创建\Sessions根目录。
9.运行\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\BootExecute中的程序。
10.根据\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\PendingFileRename Operations中指定的信息,处理尚未完成的文件改名操作。
11.初始化页面文件。
12.初始化注册表的其余部分。
13.运行\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\SetupExecute中的程序。
14.打开已知DLL(\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\Session Manager\Known DLLs),将它们映射为永久内存区。
15.创建一个线程来响应会话创建请求。
16.创建Smss来初始化会话0。(非交互会话)。
17.创建Smss来初始化会话1。(交互会话)。
一旦这些步骤完成,Smss将永远在会话0的Scrss.exe实例的句柄上等待。因为Csrss被标记为关键进程,所以,如果Scrss退出,这一等待操作将永远不会完成,因为此时系统会崩溃。
会话启动的Smss实例将完成以下事项:
1.调用NtSetSystemInformation, 请求建立起内核模式的会话数据结构。这又进而调用到内部的内存管理器函数MmSessionCreate,它建立起会话虚拟地址空间,其中包含该会话的换页内存池,以及由Windows子系统的内核模式部分( Win32k.sys)和其他的会话空间设备驱动程序所分配的属于每个会话的数据结构。
2.为该会话创建子系统进程(默认情况下,为子系统进程Csrss.exe)。
3.创建Winlogon实例或者Wininit实例。
Windows初始化进程( Wininit.exe )
Wininit.exe进程执行下面的系统初始化功能:
- 将自己标记为“关键的”,因而,如果它过早地退出,并且系统是在调试模式下引导起来的,那么,它将会在调试器中断下来(若不然,系统将崩溃)。
- 初始化用户模式调度设施。
- 创建%windir%\temp文件夹。
- 创建一个窗口站(Winsta0) 和两个桌面( Winlogon和Default), 以便会话0中的进程可以在其中运行。
- 创建Services.exe (服务控制管理器,SCM)。
- 启动Lsass.exe (本地安全认证子系统服务器)。
- 启动Lsm.exe (本地会话管理器)。
- 一直等待,直至系统停机。
服务控制管理器( SCM)
Windows中的 “服务(service)” 既可以指服务器进程,也可以指设备驱动程序。这一节讲的服务是指用户模式的进程。服务就像UNIX中的“守护进程( daemon process)”或者VMS中的“分派进程( detached process)”,这些进程可以被配置成能在系统引导时自动启动起来,而不要求有交互式的登录过程,也可以被手工启动(比如,通过运行“服务(Services)” 管理工具,或者调用Windows的StartService函数)。在通常情况下,这些服务并不与登录用户进行交互,尽管在特殊条件下这也是有可能的。
服务控制管理器是一个特殊的系统进程,它运行的映像文件是%SystemRoot%\System32\Services.exe,它负责启动、停止服务进程,也负责与这些服务进程进行交互。所谓服务程序,实际上只是调用了一些特殊Windows函数的Windows映像文件。它们通过这些特殊的Windows函数,与服务控制管理器进行交互,以便执行诸如此类的动作:注册一个服务的成功启动、响应状态请求,以及暂停或停止一个服务。Windows服务定义在注册表的HKLM\SYSTEM\CurrentControlSet\Services下。
记住,服务有三种名称:在系统中看到的正在运行的进程名称、注册表中的内部名称,以及在“服务”管理工具中给出的显示名称(并不是所有的服务都有显示名称——如果一个服务没有显示名称,则显示内部名称)。在Windows中,服务还可以有一个描述域( description field),以进一步详细说明该服务所做的事情。
要想从一个服务进程映射到该进程所包含的服务,可以使用tlist /s或tasklist /svc命令。然而,请注意,服务进程和所运行的服务之间并不总是一一对应的,因为有的服务与其他的服务共享一个进程。在注册表中,一个服务的类型代码指明了该服务是运行它自己的进程,还是与同一映像中的其他服务共享一个进程。
本地会话管理器(Lsm.exe)
本地会话管理器(Lsm.exe)管理本地机器上的中断服务器会话的状态。它通过ALPC端口SmSsWinStationApiPort向Smss发生请求启动新的会话。它通知Csrsss诸如建立连接】断开连接、终止等事件,也广播系统消息。对于以下的事件,它接受Winlogon的通知:
- 登录(Logon)和注销(Logoff)
- Shell启动和注销
- 连接到一个会话
- 与一个会话断开连接
- 锁住或解锁桌面
Winlogon、LogonUI和Userinit
Windows登录进程(%SystemRoot%\System32\Winlogon.exe) 处理交互式用户的登录和注销。当安全注意序列(SAS, Secure Attention Sequence)组合键被按下时,Winlogon就 会接到一个用户登录请求。在Windows. 上默认的SAS是组合键Ctrl+Alt+Delete。使用SAS是为了保护用户免受那些模拟登录过程的口令窃听程序欺骗,因为用户模式应用程序不可能截取这一-键盘序列。
登录过程的身份识别和认证是通过一种称为凭证提供者(credential provider)的DLL来实现的。标准的Windows凭证提供者实现了默认的Windows认证接口:口令和智能卡。不过,开发人员可以提供他们自己的凭证提供者来实现其他的身份识别和认证机制——比如基于声波纹(voice print)的方法,或者像指纹阅读器这样的生物采集设备——以 替换Windows标准的用户名/口令方法。因为Winlogon是系统依赖的一个关键系统进程,所以,凭证提供者和显示登录对话框的UI都运行在Winlogon的一个子进程中,称为LogonUI.当Winlogon检测到SAS时,它就启动这一进程,该进程会初始化凭证提供者。一旦用户输入了凭证,或者取消了登录界面,LogonUI进 程就终止。
而且,Winlogon可以加载那些需要执行二级认证的附加网络提供者DLL。这种能力使得多个网络提供者可以在正常的登录过程中一次采集到所有的身份识别和认证信息。
一旦获取到用户名和口令,就可以将它们送到本地安全认证服务器进程(%SystemRoot%\System32\Lsass.exe)进行认证。LSASS调用适当的认证包(实现为DLL的形式)执行实际的验证工作,比如检查该口令是否与存储在活动目录或者SAM(属于注册表的一部分,其中包含了关于本地用户和组的定义)中的口令相符。
在成功地完成了认证以后,LSASS调用安全引用监视器中的一个函数(例如,NtCreateToken),以生成一个访问令牌对象,该访问令牌对象包含了当前用户的安全轮廓(security profile)。 如果系统使用了UAC (用户账户控制),并且正在登录的用户是管理员组的成员或者具有管理员特权,那么,LSASS将创建该令牌的另一 个受限版本。然后,Winlogon利用此访问令牌来创建该用户会话中的初始进程。这一(或这些)初始进程被存储在注册表键HKLM\SOFTWARE\Mcrosoft\Windows\ NT\CurrentVersion\Winlogon下的注册表值Userinit中。(默认是Userinit.exe,不过,在该注册表值中可以列出多个映像名称。)
Userinit执行用户环境的一些初始化工作(比如运行登录脚本、应用组策略),然后在注册表中查找Shell值(在上一段提到的Winlogon键的下面),并且创建一个进程来运行系统定义的外壳(shell) 程序(默认是Explorer.exe)。然后,Userinit退 出。这正是Explorer.exe在进程树中没有父进程的原因——它的父进程已经退出,如前所述,tlist将所有父进程己不在运行的进程左对齐。(看待这一现象的另一 种方法是: Explorer是 Winlogon的孙子进程。)
Winlogon不仅在用户登录和注销的时候是活动的,无论何时,只要它截取到键盘的SAS就是活动的。例如,在你登录进来后,若按下了Ctrl+Alt+Delete组合键,则Windows安全屏幕会出现,提示注销、启动任务管理器、锁定工作站、关闭系统等选项。Winlogon和LogonUI是负责处理这一交互过程的进程。