Windows NT 中的服务器进程被称作子系统。模块化和结构化的程序设计都是优秀软件管理的原则,NT 选择使用客户机-服务器的体系结构显示了它对这种原则的服从。Windows NT 本可以将所需的 APIs 在内核实现,也可以在内核上加上不同的层来实现不同的 APIs。出于维护性和扩展性的目的,NT 团队选择了子系统的办法。
Win32子系统
Win32 子系统是最重要的子系统。其他的诸如 WOW 和 OS/2 子系统都主要是为了兼容性而提供的,而 POSIX 子系统在功能上又受限制(例如,POSIX 应用程序不能访问存在的任何网络)。Win32 子系统之所以重要是因为她控制着显示设备的访问大权。另外,其它的子系统实际上是 Win32 子系统上的应用程序,它们都使用 Win32 API 来实现自己的 API。本质上讲,所有的子系统都是以一个核心的 Win32 子系统为基础的。
Win32子系统组成部分(windows nt 3.51)
1.csrss.exe:用来服务user和gdi调用的用户模式服务进程
2.kernel32.dll
包含了stub函数,这些stub函数调用ntdll.dll中相应的函数,这些函数又转而调用内核中的系统调用代码
3.user32.dll
这是另一个用于win32子系统的客户端dll,其中的大部分函数都是stub函数,这些函数将调用转化为一个向服务器进程的LPC
4.gdi32.dll
与图形用户界面相关的函数都由另一个win32子系统的客户端dll来处理,gdi32中的函数与user32.dll中类似,用来向服务器进程调用LPC的stub函数
在windows nt 4.0和windows2000下,csrss的功能转移到了一个内核模式驱动win32k.sys中,user32和gdi32使用函数接口来调用win32k.sys中的服务
核心
由hal、内核和nt执行体组成。核心的分层是逻辑进行的,物理上,只有hal是一个单独的模块,内核与nt执行体和系统调用都被打包进一个ntoskrnl.exe中。
hal:将硬件的差异对其之上的层(kernel和设备驱动程序)隐藏起来。
内核kernel:提供非常原始且基本的服务,如多处理的同步、线程调度、中断分发等。
内核是仅有的不能被抢先或换出的核心组件。实际上,换出内核是可以的,但在换入的时候有一个问题。内核要负责处理页错误并将所需要的页从二级存储调进内存。因此,内核本身不能被换出,否则换出后就不能被换入。 同样的问题也使得支持交换空间的磁盘驱动也不能被分页。因为内核和设备驱动都使用 HAL 服务,所以 HAL 很自然就不是非抢先的。
nt执行体:Windows NT 核心的大部分都属于 NT executive。它位于内核之上,并向外界提供了复杂的接口。其功能组件有
1.对象管理器
2.IO管理器
3.安全引用监视器
4.虚拟内存管理器
虚拟内存的概念是现代操作系统的关键概念之一,其背后的思想是这样的。若是操作系统在执行应用程序时,将整个程序都加载内存中,则程序的大小就要受物理内存的限制。对此问题的一个直接的解决办法就是,并不将整个程序一次都加载到内存,而是只加载所需要的部分。此解决方案的事实依据是引用的本地性现象。(引用的本地性,指的是在一个较短的时间范围内,进程只访问一小部分的内存单元,即访问只局限于小部分的内存页面)
操作系统只需要将进程的工作集保持在内存中就行了。剩下的进程地址空间由二级存储器上的交换空间来支持。若进程虚访问了被换出的内存单元,则虚存管理器负责将页从二级存储器换入主存中。虚存管理器还负责为进程提供单独的地址空间使得进程不会彼此影响。虚存管理器还负责提供共享内存支持和内存映射文件。Cache Manager 就使用了虚存管理器的内存映射接口。(工作集就是一组内存页,这组内存页需要驻留在进程的内存中以在执行时不会发生太多的缺页错误。却页错误就是操作系统试图访问换出的内存单元时操作系统收到的硬件异常。)
LPC
LPC 是子系统和其客户进程之间通讯的机制。客户线程当需要子系统的服务时就会调用 LPC。LPC 将调用的参数传递给服务器进程。服务器线程执行服务并使用 LPC 将结果传回给客户端线程。
win32k.sys:内核架构的改变
在nt3.51中,KERNEL32.DLL 调用都要经过 NTDLL.DLL 转换为系统调用,而 GDI 和 USER 调用都传递到 Win32 子系统进程(LPC方式传递给csrss.exe)。
在 Windows NT 4.0 中,微软将整个 Win32 子系统移进了内核空间中,试图以此提高性能。一个新的设备驱动,WIN32K.SYS 实现了 Win32 API。Win32 API 转去执行系统调用而没有使用 LPCs。这些系统调用调用了新的 WIN32K.SYS 驱动中的函数。将服务移出子系统进程就避免了处理服务请求所需的上下文切换。在 Windows 3.51 下,每一个对 Win32 子系统的调用都需要两次上下文切换:一次是从客户线程且换到子系统线程, 再一次是从子系统线程切换回客户线程。Windows 2000 继续使用了 Win32 子系统的内核实现。