[笔记]深入解析Windows操作系统《二》系统架构(三)

本文涉及的产品
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
简介: [笔记]深入解析Windows操作系统《二》系统架构(三)

内核

内核是由Ntoskrnl.exe中的一组函数以及对于硬件体系架构的低层支持(比如中断和异常分发)构成的。Ntoskrnl.exe中的这组函数提供了一些最为基本的机制,比如线程调度和同步服务,供执行体组件使用:而对硬件的低层支持则随处理器架构的不同而有所区别。内核代码主要是用C编写的,对于那些要用到特殊的处理器指令和寄存器(不容易在C代码中访问)的任务,则保留使用汇编代码的形式。

如同上一部分中提到的各种执行体支持函数一样,内核中的许多函数也在WDK中有相应的文档描述(通过搜索以K打头的函数可以找到),因为在实现设备驱动程序的时候也需要用到它们。

内核对象

内核提供了一组定义明确的、可预知的操作系统低层原语和机制,从而使得执行体中的高层组件可以做它们需要做的事情。内核实现了操作系统的基本机制,并且避免各种策略决定,从而将自己与执行体的其余部分分离开。它几乎将所有的策略决定都留给了执行体,唯一的例外是线程调度和分发,这是由内核自己来实现的。

从内核外部来看,执行体将线程和其他可共享的资源都表示为对象。这些对象需要一些策略开销,比如用以维护它们的对象句柄(object handle),以及用以保护它们的各种安全检查,还有相应的资源配额(当它们被创建时资源配额就会被扣除)。这些开销在内核中是不存在的,内核实现了一组更为简单的对象,称为内核对象(kernel object)。,它们帮助内核控制好中心处理过程,并且支持执行体对象的创建工作。绝大多数执行体层的对象都封装了一个或者多个内核对象,把它们的内核属性合并在一起。

一组称为控制对象(control object)的内核对象建立了有关控制各种操作系统功能的语义。

这包括APC对象、DPC(Deferred Procedure Call,延迟过程调用)对象,以及I/O管理器使用的一些对象,比如中断对象等。

另一组称为分发器对象(dispatcher object)。的内核对象融合了同步的能力,可以改变或者影响线程的调度。分发器对象包括内核线程、互斥体(内部称为突变体即mutant)、事件、内核事件对(event pair)。、信号量(semaphore)、定时器,以及可等待的定时器(waitable timer)。

执行体利用内核函数来创建和维护内核对象实例,并且构建更加复杂的、提供给用户模式的对象。第3章中将更加详细地介绍对象,第5章介绍进程和线程。

内核处理器控制区和控制块(KPCR和KPRCB)

内核使用一个称为处理器控制区(processor control region,KPCR)的数据结构来存放与处理器有关的数据。KPCR包含了基本的信息,例如处理器的中断分发表(DT)、任务状态段

(TSS)和全局描述符表(GDT)。它也包括中断控制器的状态,这是内核与其他模块(比如ACPI驱动程序和HAL)共享的数据。为了便于访问KPCR,在32位Windows上,内核在f寄存器中保存了一个指向KPCR的指针:在x64 Windows系统上,指向KPCR的指针存放在gs寄存器中。在IA64系统上,KPCR总是位于0xe0000000fff0000。

KPCR也包含一个称为内核处理器控块(kernel processor control block,KPRCB)的内嵌数据结构。KPCR是已经文档化的数据结构,因而第三方的驱动程序和其他的Windows内核组件可以使用:与此不同的是,KPRCB是一个私有的数据结构,仅仅Ntoskrnl…exe中的内核代码使用该结构。KPRCB包含了调度信息(比如在该处理器上正在调度的当前线程、下一个执行的线程以及空闲线程)、该处理器的分发器数据库(其中包含了每个优先级的就绪队列)、 DPC队列、CPU厂商和标识符信息(型号(model)、步进(stepping)、速度、特征位)、CPU和NUMA拓扑(节点信息、每个芯片的核、每个核的逻辑处理器,等等)、缓存大小、时间计数信息(比如DPC和中断时间),等等。KPRCB还包含了所有关于该处理器的统计信息,比如/O统计、缓存管理器的统计(相关描述参见本书下册第11章“缓存管理器”)、DPC统计,以及内存管理器的统计(更多信息参见本书下册第10章)。最后,KPRCB有时候也被用来存储一些缓存对齐的、针对每个处理器的数据结构,以便于优化内存访问,尤其是在NUMA系统上。

例如,系统中非换页的和换页的内存池快查表也存储在KPRCB中。

实验:观察KPCR和KPRCB

利用pcr和prcb内核调试器命令,可以查看KPCR和KPRCB的内容。如果在调试器命令中不指定任何标志,那么,调试器默认显示CPU0的信息:否则,可以在调试器命令的后面加上一个CPU编号,从而指定该CPU,例如pcr2.下面的例子显示了pcr和prcb命令的输出。如果系统有正在等待的DPC,也会显示出来。

可以使用dt命令,直接将KPCR和KPRCB数据结构转储出来,因为上述两个调试器命令已经给出了数据结构的地址(在上面的输出中用粗体显示)。例如,如果想要知道处理器的速度,可以用下面的命令来查看MHz域

在这台机器上,处理器以3GHz的速度运行。

硬件支持

内核的另一个主要任务是将执行体和设备驱动程序从Windows所支持的各种硬件体系架构的差异中抽象或隔离出来。这项任务包括处理各种功能(比如中断处理、异常分发和多处理器同步)方面的变化情况。

即便是对这些与硬件相关的功能,在设计内核时也力图使公共代码尽可能最大化。内核支持一组可移植的接口,这组接口的语义

在不同的体系架构上是等同的。而且,实现这组可移植接口的大部分代码在不同的体系架构上也是等同的。

然而,在这组接口中,有些在不同的体系架构上有不同的实现:或是在有些接口的实现中,部分代码与体系架构相关。这些独立于体系架构的接口可以在任何一台机器上被调用,而且无论实现代码是否随体系结构的不同而不同,接口的语义总是相同的。有些内核接口(比如自旋锁例程,将在第3章中讲述)实际上是在HAL(在下一节讲述)中实现的,因为它们的实现即使在同一体系架构族的系统中也可能有所不同。

内核中有一小部分代码涉及与x86有关的接口,之所以需要这部分代码,是为了支持老的MS-DOS程序。这些x86接口并不是可移植的,因为在任何其他体系架构的机器上它们都不可能被调用:而且它们根本不会出现在这样的机器上。例如,与x86相关的代码提供了相应的功能调用,可用来维护全局描述符表(GDT)和局部描述符表(LDT),这正是x86的硬件特性。

在内核中与体系架构相关的代码的另一个例子是,提供“转译缓冲区(translation buffer)”

和“CPU缓存”支持的接口。为了提供这样的支持,不同的体系架构需要不同的代码,因为处理器缓存的实现方式各有不同。

内核中有一小部分代码涉及与x86有关的接口,之所以需要这部分代码,是为了支持老的MS-DOS程序。这些x86接口并不是可移植的,因为在任何其他体系架构的机器上它们都不可能被调用:而且它们根本不会出现在这样的机器上。例如,与x86相关的代码提供了相应的功能调用,可用来维护全局描述符表(GDT)和局部描述符表(LDT),这正是x86的硬件特性。

在内核中与体系架构相关的代码的另一个例子是,提供“转译缓冲区(translation buffer)”和“CPU缓存”支持的接口。为了提供这样的支持,不同的体系架构需要不同的代码,因为处理器缓存的实现方式各有不同。

另一个例子是环境切换。尽管从高层来看,线程选择和环境切换可以使用同样的算法(上一个线程的执行环境被保存起来,新线程的环境被加载进来,然后新线程被启动执行),但在不同的处理器上,具体的实现还是存在体系架构方面的差异。因为执行环境是由处理器的状态(寄存器等)来描述的,所以哪些信息应该被保存或加载,随体系架构的不同而有所不同。

硬件抽象层(HAL)

正如本章开始时所提到的,Windows设计的关键要素之一是,它能被移植到各种不同的硬件平台上。硬件抽象层(HAL)是使得这种可移植性成为可能的一个关键部分。HAL是一个可加载的内核模式模块(Hal.dl),它提供了针对Windows当前运行所在的硬件平台的低层接口。它隐藏了与硬件相关的细节,比如/O接口、中断控制器,以及多处理器通信机制一一任何与体系架构相关或者与机器相关的功能。

所以,Windows内部组件以及用户编写的设备驱动程序并不直接访问硬件:而是当需要获得与平台相关的信息时,通过调用HAL例程来保持可移植性。出于这一原因,这些HAL例程在WDK中也被文档化了。更多有关HAL及其在设备驱动程序中用法的信息,参见WDK。

虽然Windows附带了几个HAL(见表2.4),但是它有能力在引导时检测到应该使用哪个HAL,因而,在早期Windows版本上“试图在不同类型的系统上引导已安装的Windows.系统”的问题便不复存在。

实验:确定当前正在运行哪个HAL

利用WinDbg,可以确定当前正在运行HAL的哪个版本,做法是,打开一个本地内核调试会话,确保已经加载了符号(输入.reload),然后输入1 m mv ha1命令。例如,下面的输出来自于一个运行ACPI HAL的系统:

实验:查看NTOSKRNL和HAL映像的依赖关系

可以利用Dependency Walker.工具(Depends.exe)来检查内核和HAL映像文件的导出和导入表,从而了解它们之间的关系。要在该工具中检查映像文件,可以从File菜单中选择Open命令来打开目标映像文件。

使用该工具来查看Ntoskrnl的依赖性,可能看到如下图所示输出:

注意,Ntoskrnl链接了HAL,而HAL又链接了Ntoskrnl(它们相互使用了对方的函数)。

Ntoskrnli还链接了以下二进制组件:

  • Pshed.dl,针对特定平台的硬件错误驱动程序。PSHED对底层平台的硬件错误报告设施进行了抽象,做法是,将平台的错误处理机制的细节与操作系统隔离开,仅仅向Windows操作系统暴露一个一致的接口。
  • (仅限于在32位系统上)Bootvid.dll,引导视频驱动程序。Bootvid提供了在系统启动过程中对VGA命令的支持,以便显示引导文本和引导标志图案。在x64系统上,该库被内联到内核模块中,以避免与内核补丁保护(KPP,Kernel Patch Protection)机制发生冲突。(有关KPP和PatchGuard的更多信息,参见第3章。)
  • Kdcom.dll,内核调试器协议(KD)通信库。
  • Ci.dll,代码完整性库(有关代码完整性的更多信息,参见第3章)。
  • CIfs.sys,公共的日志文件系统驱动程序,也用于内核事务管理器(KTM)。(有关KTM的更多信息,参见第3章。)
    有关这一工具所显示信息的详细描述,参见Dependency Walker的帮助文件(Depends.hlp)。

设备驱动程序

本书下册第8章才会详细介绍设备驱动程序,这一节先大致介绍一下驱动程序的类型,并说明如何列出系统中已经安装和加载的驱动程序。

设备驱动程序是可加载的内核模式模块(通常以.sys结尾),它们在I/O管理器和相应的硬件之间建立起连接。设备驱动程序运行在内核模式下,位于以下三种执行环境之一:

  • 在发起IO功能的用户线程的环境中。
  • 在内核模式系统线程的环境中。
  • 作为中断的结果(因此它不在任何特定的进程或者线程的执行环境中一一当该中断产生时,无论当前进程或者线程是哪个)。

正如上一节所述,Windows中的设备驱动程序并不直接维护硬件,而是调用HAL中的函数与硬件打交道。驱动程序往往是用C(有时候用C++)来编写的,因此,通过正确地使用HAL例程,驱动程序可以在Windows)所支持的CPU体系架构间进行源代码级的移植,而在同一个体系架构族内则是二进制可移植的。

设备驱动程序有以下几种类型:

  • 硬件设备驱动程序(hardware device driver)通过HAL操纵硬件,从而将输出写到物理设备或网络中,或者从物理设备或网络上接收输入。硬件设备驱动程序也有许多类型,比如总线驱动程序、人机界面驱动程序、大容量存储设备驱动程序,等等。
  • 文件系统驱动程序(file system driver)是指这样的Windows驱动程序:可以接受面向文件的/O请求,并且将这些请求转换成针对某一特定设备的/O请求。
  • 文件系统过滤驱动程序(file system filter driver),比如那些执行磁盘镜像和加密的驱动程序,或者那些截取I/O请求并执行某些增值处理之后再把I/O传递给下一层的驱动程序。
  • 网络重定向器(network redirector)和服务器,分别指那些将文件系统I/O请求传递给网络上某台机器,或者从网络上接收此类请求的文件系统驱动程序。
  • 协议驱动程序(protocol driver),实现诸如TCP/IP、NetBEUI和IPX/SPX之类的网络协议。
  • 内核流式过滤驱动程序(kernel streaming filter driver),这样的驱动程序被串接起来对数据流进行信号处理,比如录制或者播放视频和音频。

要在系统中添加用户编写的内核模式代码,安装驱动程序是唯一的方法,所以有些程序员把编写设备驱动程序当作是一种访问操作系统内部函数和数据结构的简便方法(在用户模式下它们是不可访问的,但是在WDK中有文档以及相应的支持)。例如,Sysinternals的许多工具是由Windows GUI)应用程序和设备驱动程序组成的,其中驱动程序用于收集系统的内部状态,以及调用那些只可在内核模式下访问的函数(从用户模式Windows API中无法访问)。

Windows驱动模型

Windows2000增加了对即插即用、电源选项的支持,同时也扩展了Windows NT的驱动程序模型,新的模型称为WDM(Windows Driver Model,Windows驱动程序模型)。Windows200d及以后的版本都可以运行老的Windows NT4驱动程序,但是因为这些驱动程序不支持即插即用和电源选项,所以运行这些驱动程序的系统在这两方面的能力都会有所退化。

从WDM的角度来看,有以下三种驱动程序。

  • 总线驱动程序(bus driver)为总线控制器、适配器、桥或任何带有子设备的设备提供服务。总线驱动程序是必需的驱动程序,通常Microsoft:会提供此类驱动程序:系统中的每一种总线类型(比如PCI、PCMCIA和USB)都有一个总线驱动程序。第三方也可以编写总线驱动程序来为新的总线提供支持,比如VMEbus、Multibus和Futurebus。
  • 功能驱动程序(function driver)是主要的设备驱动程序,它为相应的设备提供可操作的接口。功能驱动程序也是必需的,除非相应的设备可以按照原始的方式直接使用(指一种特殊的实现,其/O可通过总线驱动程序和总线过滤驱动程序来完成,比如SCSI PassThru)。根据定义,功能驱动程序最了解具体的设备,它往往是唯一能访问与该设备相关的寄存器的驱动程序。
  • 过滤驱动程序(filter driver)用来为某一设备(或已有的驱动程序)增加新的功能,或者修改来自其他驱动程序的/O请求或应答(也常常用来修补那些未能提供正确的硬件资源需求信息的硬件设备)。过滤驱动程序是可选的,可以有任意数目,可以放在功能驱动程序之上或之下,也可以放在总线驱动程序之上。通常,系统原始设备制造商(OEM)或者独立硬件供应商(HV)会提供过滤驱动程序。

在WDM驱动程序环境中,对于设备而言,并不是由单个驱动程序来控制它的所有方面:总线驱动程序负责向PP管理器报告其总线上的设备,而功能驱动程序用于操纵该设备。

在大多数情况下,较低层的过滤驱动程序用于改变设备硬件的行为。例如,若一个设备向其总线驱动程序报告它需要4个I/O端口,但实际需要16个/O端口,那么与该设备相关的低层功能过滤驱动程序可以截获总线驱动程序向PP管理器报告的硬件资源列表,并更新IO端口的数目。

较上层的过滤驱动程序通常为设备提供一些增值特性,比如一个针对键盘的上层过滤驱动程序可以强制加上额外的安全检查。

关于中断处理将在第3章中解释。有关I/O管理器、WDM、即插即用和电源选项的详细介绍,参见本书下册第8章。

Windows驱动程序基础(WDF)

Windows驱动程序基础(WDF,Windows Driver Foundation)简化了Windows!驱动程序的开发,它提供两个框架:内核模式驱动程序框架(KMDF,Kernel–Mode Driver Framework)和用户模式驱动程序框架(UMDF,User-Mode Driver Framework)。开发人员可以用KMDF来为Windows2000SP4及以后的系统编写驱动程序,而UMDF仅支持Windows XP及以后的系统。

KMDF提供了一个简单的WDM接口,向驱动程序开发人员隐藏了WDM的复杂性,开发人员无须修改底层的总线/功能/过滤驱动程序模型。KMDF驱动程序响应它们登记过的事件,并调用KMDF库来完成那些并不特定于它们所管理硬件的工作,比如一般性的电源管理或同步(以前,每个驱动程序必须要实现它自己的一份管理工作)。在有些情况下,200多行的WDM代码可以用一个简单的KMDF函数调用来替代。

UMDF使得某些特定类型的驱动程序(主要是基于USB或其他高延迟协议的总线)可以实现为用户模式的驱动程序,比如摄像机、MP3播放器、移动电话、PDA以及打印机的驱动程序。

UMDF运行每个用户模式驱动程序,本质上这是一个用户模式服务,它使用ALPC与真正访问硬件的内核模式包装驱动程序进行通信。如果一个UMDF驱动程序崩溃了,它的进程将死掉,通常还会重新启动,所以系统不会招致不稳定一一仅仅在宿纳该驱动程序的服务进程重新启动的过程中该设备不可用。最后,UMDF驱动程序是用C+按照COM风格的类和语义来编写的,可以进一步降低程序员编写设备驱动程序的门槛。

相关文章
|
12天前
|
设计模式 算法 安全
实时操作系统(RTOS)深度解析及Java实现初探
【10月更文挑战第22天】实时操作系统(RTOS,Real-Time Operating System)是一种能够在严格的时间限制内响应外部事件并处理任务的操作系统。它以其高效、高速、可靠的特点,广泛应用于工业自动化、航空航天、医疗设备、交通控制等领域。本文将深入浅出地介绍RTOS的相关概念、底层原理、作用与功能,并探讨在Java中实现实时系统的方法。
42 1
|
18天前
|
存储 弹性计算 运维
阿里云国际Windows操作系统迁移教程
阿里云国际Windows操作系统迁移教程
|
4天前
|
边缘计算 自动驾驶 5G
|
7天前
|
存储 安全 Java
系统安全架构的深度解析与实践:Java代码实现
【11月更文挑战第1天】系统安全架构是保护信息系统免受各种威胁和攻击的关键。作为系统架构师,设计一套完善的系统安全架构不仅需要对各种安全威胁有深入理解,还需要熟练掌握各种安全技术和工具。
33 10
|
4天前
|
消息中间件 编解码 开发者
深入解析 Flutter兼容鸿蒙next全体生态的横竖屏适配与多屏协作兼容架构
本文深入探讨了 Flutter 在屏幕适配、横竖屏切换及多屏协作方面的兼容架构。介绍了 Flutter 的响应式布局、逻辑像素、方向感知、LayoutBuilder 等工具,以及如何通过 StreamBuilder 和 Provider 实现多屏数据同步。结合实际应用场景,如移动办公和教育应用,展示了 Flutter 的强大功能和灵活性。
71 6
|
2天前
|
存储 物联网 调度
操作系统的心脏:内核深度解析
在数字世界的构建中,操作系统扮演着基石的角色,而其核心—内核,则是这一复杂系统的灵魂。本文将深入探讨操作系统内核的工作原理,揭示它是如何管理硬件资源、运行程序以及提供系统服务的。通过理解内核的结构和功能,我们可以更好地把握计算机系统的运作机制,进而优化和创新我们的技术实践。
|
4天前
|
存储 SQL 缓存
AnalyticDB 实时数仓架构解析
AnalyticDB 是阿里云自研的 OLAP 数据库,广泛应用于行为分析、数据报表、金融风控等应用场景,可支持 100 trillion 行记录、10PB 量级的数据规模,亚秒级完成交互式分析查询。本文是对 《 AnalyticDB: Real-time OLAP Database System at Alibaba Cloud 》的学习总结。
17 1
|
6天前
|
监控 安全 Serverless
"揭秘D2终端大会热点技术:Serverless架构最佳实践全解析,让你的开发效率翻倍,迈向技术新高峰!"
【10月更文挑战第23天】D2终端大会汇聚了众多前沿技术,其中Serverless架构备受瞩目。它让开发者无需关注服务器管理,专注于业务逻辑,提高开发效率。本文介绍了选择合适平台、设计合理函数架构、优化性能及安全监控的最佳实践,助力开发者充分挖掘Serverless潜力,推动技术发展。
17 1
|
10天前
|
监控 网络协议 安全
DNS服务器故障不容小觑,从应急视角谈DNS架构
DNS服务器故障不容小觑,从应急视角谈DNS架构
32 4
|
11天前
|
缓存 前端开发 JavaScript
前端的全栈之路Meteor篇(二):容器化开发环境下的meteor工程架构解析
本文详细介绍了使用Docker创建Meteor项目的准备工作与步骤,解析了容器化Meteor项目的目录结构,包括工程准备、环境配置、容器启动及项目架构分析。提供了最佳实践建议,适合初学者参考学习。项目代码已托管至GitCode,方便读者实践与交流。