《深入解析Windows操作系统》读书笔记(2)
Windows NT设计小组在项目开始之初确定的设计目标:
- 扩展性。在市场需求变化后能够自如的增长和改变。
- 可移植性。必须能运行在多种硬件体系结构上,出现新的硬件体系结构后,应该可以很容易迁移。
- 可靠性和健壮性。系统应该能够保护自己,不能因内部的错误和外部的篡改而不能工作。应用程序应该无法伤害操作系统或者其他的应用程序。
- 兼容性。和MS之前的操作系统兼容,包括DOS、Winsows98等;要和其他的操作系统兼容,包括OS/2和NerWare。
- 性能。在其他目标的约束下,尽可能快。
Windows NT不是微内核系统。微内核系统中,主要组件运行在各自独立的进程中,它们有自己私有的地址控件,在这一组组件之上是微内核提供的一组原语服务。纯粹的微内核设计在商业上是不切实际的,由于需要不停的在内核模式和用户模式之间进行切换,所以效率是很低的。
Windows是一个对称多处理(SMP,Sysmmetric multiprocessing)操作系统,在这些处理器中没有主处理器,所有的处理器共享唯一的内存空间。
Windows NT整体结构分成用户模式和内核模式两部分。用户模式主要包括一些进程以及子系统DLL;内核模式包括Windows执行体、Windows内核、设备驱动程序、硬件抽象层和窗口图形系统。
用户模式下的进程分为以下四种:
- 系统支持进程。例如登陆或者进程管理器,这些进程并不是Windows的服务,它们不是由服务管理器来启动的。
- 服务进程。指Windows服务,它的运行一般独立于用户登陆。
- 用户应用程序。
- 环境子系统服务器进程。实现了操作系统环境的支持部分。这里的环境是指操作系统展示给用户或者程序员的个性化部分。
在Windows下,用户应用程序并不直接调用原始的Windows操作系统服务,而是通过一个或者多个子系统动态链接库(DLLs)来发起调用。
子系统DLL的角色:将一个已文档化的函数转化为一些恰当的内部Windows系统服务调用。
内核模式组件包括以下几部分:
- Windows执行体(executive)。包含了基本的操作系统服务,例如内存管理、进程和线程管理、安全性、I/O、网络和跨进程通信。
- Windows内核(kernel)。由一组低层次的操作系统功能构成,例如线程调度、中断和异常分发。它也提供了一组例程和基本对象。执行的其余部分利用这些例程和对象实现更高层次的功能。
- 设备驱动程序(device drivers)。既包括硬件设备驱动程序,也包括文件系统和网络驱动程序。其中硬件设备却动程序将用户的I/O函数调用转换成特定的硬件设备I/O请求。
- 硬件抽象层(HAL,Hardware Abstraction Layer)。一层特殊的代码,它把内核、设备驱动程序和Windows执行体的其余部分,跟与平台相关的硬件差异隔离开来。
- 窗口和图形系统(windowing and graphic system)。实现了图形用户界面(GUI)函数,例如对窗口的处理、用户界面控件等。
Windows系统对于可移植性的支持,体现在以下两方面:
- 采取分层设计。系统的高层部分可以不考虑体系结构之间的差别,也不用关心硬件平台的差异。
- Windows的绝大部分代码使用C语言编写,少部分用c++编写,极少部分采用汇编。
Windows最初有3个环境子系统:OS/2、POSIX和Windows。3个子系统中Windows子系统是很特殊的,Windows非它不能运行,其他两个子系统被配置成按需启动,而Windows子系统则必须总是在运行。
环境子系统启动信息背包存在注册表键HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\SubSystems下。
环境子系统的角色:将Windows基本系统服务的部分子集暴露给应用程序。每个子系统都提供了对于Windows原生服务不同部分的访问能力,这意味着建立在某一个子系统上的应用程序可以做到的事情,是另一个建立在不同子系统上的应用无法做到的。不同的子系统之间,函数调用不能混合。
当一个应用程序调用子系统DLL中的某个函数时,情况分为以下三种:
- 该函数完全是在该子系统DLL中实现的,在用户模式下运行。
- 该函数要求调用Windows执行体一次或者多次。
- 该函数要求在环境子系统进程中完成某些工作在这种情况下,该函数以消息的形式给环境子系统发送一个客户/服务器请求,从而让环境子系统执行某个操作,然后子系统DLL等待应答,收到应答后再返回给调用者。
Windows子系统由以下几个主要组件构成:
- 环境子系统进程(Csrss.exe)。包括:1. 控制台窗口;2. 创建或删除进程和线程;3. 对16位虚拟DOS机进程的一部分支持;4. 其他的一些函数,例如GetTempFile、DefineDosDevice等。
- 内核模式设备驱动程序(Win32k.sys)。包括:1. 窗口管理器;2. 图形设备接口。
- 子系统DLL。它们将已经文档化的Windows API函数,翻译成Ntoskrnl.exe和Win32k.sys中恰当的且绝大部分未文档化的内核模式系统服务调用。
- 图形设备驱动程序。指与硬件相关的图形显示器驱动程序。打印机驱动程序和视频微端口驱动程序。
应用程序调用标准的USERS函数以便在显示器上创建用户界面控件,窗口管理器将这些请求发送给GDI,GDI将它们传递给图形设备驱动程序,在设备驱动程序内部,这些请求被经过格式化以适合特定的显示器设备,显示器驱动与视频微端口驱动程序是成对的,以实现对视频显示的支持。
GDI函数位于应用程序和图形设备的而中间。负责解释应用程序的图形输出请求,并且将这些请求发送给图形显示器驱动程序。
在Windows NT4中,将原本运行在Windows子系统进程环境中的窗口和图形代码移到了一组可调用的系统服务中,直接运行在内核模式下。这样做的原因是为了提高总体系统性能,把Windows图形子系统放在一个分开的服务器进程中,必将导致多次线程和进程环境的切换,从而消耗掉相当数量的CPU周期和内存资源。
Ntdll.dll:一个特殊的系统支持库,主用用于子系统DLL。它包含两种类型的函数:1. 系统服务分发存根,他们会调用Windows执行体系统服务;2. 内部支持函数,供子系统、子系统DLL以及其他原生映像文件使用。
系统服务分发存根为Windows执行体系统服务提供了接口,在用户模式下可以通过这些接口函数调用Windows执行体的系统服务。对于每一个这样的函数,Ntdll包含了一个同名的入口点,函数内部的代码包含了与处理器体系结构相关的模式切换指令,通过该指令可转换到内核模式下,从而调用系统服务分发器,分发器在键校了某些参数以后,再调用真正的内核模式系统服务。
Windows执行体是Ntoskrnl.exe中的上层,它包括以下类型的函数:
- 可以再用户模式下调用的导出函数,这些函数被称为系统服务,并且通过Ntdll被导出,这些服务绝大部分可以通过Windows API来访问,或者通过另一个环境子系统的API来访问。
- 可以通过DeviceIpControl函数来调用的设备驱动器函数。
- 只能在内核模式下调用的导出函数,并且这些函数在Windows DDK或者Windows IFS Kit中已经被文档化。
- 在内核模式下调用,但未在Windows DDK或者Windows IFS Kit中文档化的导出函数。
- 定义为全局符号但是未被导出的函数。
- 未定义为全局符号,而是在一个模块内部的函数。
Windows执行体包含了以下主要的组件:
- 配置管理器。负责系统注册表的实现和管理。
- 进程和线程管理器。创建或者种植进程和线程。
- 安全引用监视器。强制在本地计算机上实行安全策略。
- I/O管理器。实现与设备无关的I/O操作,负责将这些操作分派到恰当的设备驱动程序以供进一步处理。
- 即插即用(PnP)管理器。根据每个设备的资源需求,分配适当的硬件资源,例如IRQ、DMA通道和内存位置等。
- 电源管理器。负责协调电源事件,并且向设备驱动程序产生电源管理I/O通知。
- WDM Windows管理规范例程。允许设备驱动程序可以发布有关性能和配置的信息,以及接收来自用户模式WMI服务的命令。
- 高速缓存管理器。通过让最近因用过的磁盘数据驻留在内存中以便快速访问的方法提高了以文件爱你为基础的I/O操作的性能。
- 内存管理器。实现了虚拟内存。
- 逻辑预取器。加速系统和进程的启动过程。
Windows执行体包含4组主要的支持函数,分别是:
- 对象管理器
- LPC设施:在同一台机器上的客户进程和服务器进程之间传递信息,LPC是RPC的一个更加灵活的优化版本。
- 一组涉及范围管饭的公共运行库函数。
- 执行体支持例程。
内核是由Ntoskrnl.exe中的一组函数以及对于硬件体系结构的低层支持构成的。Ntoskrnl.exe中的这组函数提供了一些最为基本的机制,例如线程调度和同步服务;而对硬件的低层支持则随着每个处理器结构的不同而有所区别。内核代码主要是C语言编写,对于那些要用到特殊的处理器指令和寄存器,并且不容易在C代码中访问的任务,则保留使用汇编代码的形式。
内核提供了一组定义明确的、可预知的操作系统低层原语和机制,从而使得执行体中的高层组件可以做它们需要做的事情。内核实现了操作系统的基本机制,并且避免了各种策略决定,从而将自己与执行体的其余部分分离开。它几乎将所有的策略决定都留给了执行体,唯一的例外就是线程调度和分发,这是由内核自己来实现的。
内核实现了一组更简单的而对象,成为内核对象,它们帮助内核控制好中心处理过程,并且支持执行体对象的创建工作。执行体层的绝大多数对象包装了一个或者多个内核对象,把它们的内核属性合并起来。
内核的另一个重要任务就是将执行体和设备驱动程序从Windows所支持的各种硬件体系结构中抽象出来,或者隔离出这些变种之间的差异,即便是对这些与硬件相关的功能,在设计内核的时候也试图是公共代码尽可能的最大化。这些与体系结构杜丽的接口可以再任何一台机器上被调用,而且无论是先代码是否随着体系结构的不同而不同,接口的语义总是相同的。
HAL是一个可加载的、内核模式的模块,它提供了针对Windows当前运行所在的硬件平台的底层接口,它隐藏了与硬件相关的细节。
设备驱动程序是可加载的内核模式模块,它们在I/O管理器和相应的硬件之间建立起链接。
设备驱动程序运行在内核模式下,位于以下三种执行环境之一:
- 在发起I/O功能的用户线程的环境中。
- 在内核模式的系统线程的环境中。
- 作为一个中断的结果。
Windows中的设备驱动程序并不直接维护硬件,而是调用HAL中函数与硬件进行交互。
设备驱动程序可以分为以下几类:
- 硬件设备驱动程序。
- 文件系统驱动程序。
- 文件系统过滤器驱动程序。
- 网络重定向和服务器。
- 协议驱动程序。
- 内核流式过滤器驱动程序。
要想在系统中添加用户编写的内核模式代码,安装驱动程序是唯一的方法,所以有些程序员把编写设备驱动程序当做是一种访问操作系统内部函数和数据结构的渐变方法。
目前,新的驱动程序模型是WDM(Windows Driver Model),从WDM的角度来看,驱动程序可以分为以下三种:
- 总线型驱动程序。它为总线控制器、适配器、桥或者任何带有子设备的设备提供服务。总线驱动程序是必需的驱动程序,通常Microsoft会提供此类驱动程序。
- 功能型驱动程序。它是主要的设备驱动程序,为相应的设备提供了可操作的接口,功能型驱动也是必需的,它最了解某一特定的设备,而且它往往是唯一能访问与该设备相关的寄存器的驱动程序。
- 过滤型驱动程序。它用来为某一设备增加新的功能,或者修改来自其他设备的I/O请求或应答,过滤型驱动程序是可选的,可以有任意数目。
在WDM驱动环境中,对于一个设备来说,并不是由单个驱动程序来控制它的所有方面:一个总线型驱动程序负责向PnP管理器报告其总线上的设备,而一个功能型驱动程序操纵该设备。
在大多数情况下,低层次的过滤型驱动程序改变设备硬件的行为,上层的过滤型驱动程序为一个设备提供一些增值特性。
以下的系统进程会出现在每一个Windows系统中:
- 空闲进程(Idle)。
- 会话管理器(Smss.exe)。
- Windows子系统(Csrss.exe)。
- 登陆进程(Winlogon.exe)。
- 服务控制管理器(Services.exe)和它创建的子服务进程(例如系统提供的通用服务宿主进程Svchost.exe)。
- 本地安全认证服务器(Lsass.exe)。
System进程是一个特殊线程的母体,这种特殊线程只能在内核模式下运行,称为内核模式系统线程。系统线程具备普通用户模式线程的所有属性和环境,但是不同的地方在于,他们只在内核模式下运行系统空间中加载的代码,无论这些代码是在Ntoskrnl.exe中,还是在任何其他加载进来的设备驱动程序。
内核会创建一个称为平衡集管理器的系统线程,它美妙被唤醒一次,从而有可能发出各种与调度和内存管理相关的事件。
会话管理器是系统中第一个创建的用户模式进程,负责完成执行体和内核初始化工作的内核模式系统线程在最后阶段创建了实际的Smss进程。
进入Windows系统的过程:首先启动Winlogon进程,等待输入用户名和密码或者其他输入;然后通过LSASS调用适当的认证包以执行实际的验证操作;在成功验证后,LSASS调用安全引用监视器中的一个函数,以生成一个访问令牌对象,该对象包含了当前用户的安全轮廓,接着,Winlogon利用次访问令牌来创建该用户会话中的初始进程;Userinit执行该用户环境中的一些初始化工作,然后在注册表中查找shell值,并且创建一个进程来运行系统定义的外壳程序(默认是Explorer.exe);最后Userinit退出,这也就说明了为什么Explorer.exe在进程树中没有父进程的原因。
Winlogon不仅当用户登录和注销时是活动的,而且无论合适,当它截取到键盘上的SAS(Ctrl + Alt + Del)时也是活动的。
Windows中的“服务”既可以指一个服务器进程,也可以指一个设备驱动程序。
服务控制管理器是一个特殊的系统进程,它运行的影响文件是\Windows\System32\Services.exe,它负责启动、停止服务进程,也负责与这些服务进程进行交互,所谓服务程序,实际上只是调用了一些特殊Windows函数的Windows映像。
服务一般有三个名称:你在系统中看到的正在运行的进程名、注册表中的内部名称和在Services管理工具中给出的显示名。