欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

[说明] 深入分析 Windows 操作系统的 "二 "系统架构

最编程 2024-03-13 12:43:34
...

文章目录



   前言

   2.1 需求和设计目标

   2.2 操作系统模型

   2.3系统架构

       系统软件层面组成

   2.4 关键系统组件

       环境子系统和子系统DLL

           子系统启动

           Windows子系统

           控制台窗口宿主进程

           UNIX应用子系统

           最初的POSIX子系统

       Ntdll.dll

       执行体

       内核

           内核对象

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

           实验:观察KPCR和KPRCB

           硬件支持

       硬件抽象层(HAL)

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

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

       设备驱动程序

           Windows驱动模型

           Windows驱动程序基础(WDF)

               实验:查看已安装的设备驱动程序

           尚未文档化的接口

       系统进程

           系统空闲进程

           System进程和系统线程

               实验:将系统线程映射到一个设备驱动程序

           会话管理器

           Windows初始化进程(Wininit.exe)

           服务控制管理器(SCM)

               实验:列出当前安装的服务

               实验:检查服务进程内部的有关服务的细节

           本地会话管理器(Lsm.exe)

           WinLogon、LogonUl和UserInit

   总结


前言



2.1 需求和设计目标



回到1989年,下面的需求驱动了Windows NT的规范:


   提供一个真正32位的、抢占式的(preemptive)、可重入的(reentrant)虚拟内存操作系统。

   在多种硬件体系架构和平台上运行。

   可在对称多处理器系统(symmetric multiprocessing systems)上运行,并且能很好地适应处理器的数量。

   成为一个极好的分布式计算平台,无论是作为网络客户机还是服务器。

   能够运行大多数己有的16位MS-DOS和Microsoft Windows3.1应用程序。

   符合*对于P0SIX1003.1兼容性的要求。

   符合*和工业界对于操作系统安全性方面的要求。

   支持Unicode,从而很容易适应全球市场。


要创建一个满足这些需求的系统,必须做出数千个决定:

为了便于做出这些决定, Windows NT设计小组在项目开始之初选择了下面的设计目标:


   扩展性:编写的系统代码必须能够随着市场需求的变化而自如的增长和改变

   可移植性:系统必须能运行在多种硬件体系架构上,必须能根据市场的需要,相对容易的迁移到新的体系架构上

   可靠性和健壮性系统应该能保护自己,不会因内部故障和外部篡改而不能工作,应用程序应该无法伤害操作系统或其他应用程序

   兼容性虽然Windows NT应该扩展已有的技术,但是它的用户界面和API应该与老版本Windows和MS-DOS兼容。而且它也应该能与其他的系统,比如Unix OS2 NetWare 很好的互操作.

   性能 在每一种硬件平台上尽可能运行得更快,对外部的响应尽可能地及时.


2.2 操作系统模型



两种模式:


   用户模式

   内核模式


2.3系统架构



基本架构

网络异常,图片无法展示
|

网络异常,图片无法展示
|


系统软件层面组成


   注册表

   事件日志

   本地组和本地安全策略


2.4 关键系统组件



Windows系统架构

图片.png


环境子系统和子系统DLL


子系统启动


子系统是由会话管理器(Session Manager)(Smss.exe)进程启动起来的,子系统的启动信息保存在注册表键

HKLMISYSTEM\CurrentControlSet\Control\Session Manager\Subsystems


的下面。

图片.png

Required值列出了系统引导时加载的子系统。

该值有两个字符串:


   Windows

   Debug。


Windows值包含了Windows子系统的文件规范,Csrss.exe,它代表了客户机/服务器运行时子系统(Client/Server Run-Time Subsystem)。


Debug值是空的(因为它被用于内部测试),因此什么也不是。Optional值表明了SUA子系统将会按需启动。注册表值Kmode包含了Windows子系统的内核模式部分的文件名称,Win32k.sys(本章后面将进一步解释)。


Windows子系统


Windows子系统由以下主要的组件构成:


   对于每个会话,环境子系统进程(Csrss.exe)有一个实例加载三个DLL(Basesrv.dll、Winsrv.dll和Csrsrv.dll),它们包含下列支持:

   – 创建或删除进程和线程。

   – 对16位DOS虚拟机(VDM)进程的部分支持(仅32位Windows)。sxs (Side-by-side)/Fusion和清单文件(manifest)支持。

   – 其他一些函数,比如GetTempFile、DefineDosDevice、ExitWindowsEx,以及几个自然语言支持函数。


   内核模式设备驱动程序( Win32k.sys)包含下列支持:

   – 窗口管理器( window manager),它控制窗口显示,管理屏幕输出,采集来自键盘、鼠标和其他设备的输入,同时也负责将用户消息传递给应用程序。

   – 图形设备接口(GDI,Graphics Device Interface),它是专门针对图形输出设备的函数库,其中包括线段、文本和图形的绘制函数,以及图形控制函数。

   – DirectX功能的包装函数,Windows对DirectX的支持是在另一个内核驱动程序( Dxgkrnl.sys)中实现的。


   控制台宿主进程(Conhost.exe),提供了对控制台(字符环境〉应用程序的支持。


   子系统DLL (比如Kernel32.dll、Advapi32.dll、User32.dll和Gdi32.dll),将已经文档化的Windows API函数,转译成Ntoskrnl.exe和Win32k.sys中恰当的且绝大多数未文档化的内核模式系统服务调用。


   图形设备驱动程序,与硬件相关的图形显示器驱动程序、打印机驱动程序和视频微端口驱动程序。


控制台窗口宿主进程


在最初的Windows子系统设计中,子系统进程(Csrss.exe)负责管理控制台窗口,每个控制台应用程序(比如Cmd.exe,“命令提示符”程序)与Csrss进行通信。现在,Windows为系统中的每个控制台窗口使用了一个单独的进程:控制台窗口宿主进程(Conhost.exe)。(多个控制台应用程序可以共享同一个控制台窗口,比如在从命令提示符窗口中发起一个新的命令提示符窗口时。默认情况下,第二个命令提示符窗口共享前者的控制台窗口。)


无论何时,只要控制台应用程序向当前会话中正在运行的Csss实例注册其自身,Csss就利用客户进程的安全令牌,而不是Csrss的System令牌来创建Conhost:实例。然后,它映射一个共享内存区,让所有的Conhosti都可以与Csrss共享一部分内存,以便于高效地处理缓冲区(因为这些线程不再运行在Csrss内部了),并且Csrss也在RPC Control对象目录中创建一个命名的异步本地过程调用(ALPC,Asynchronous Local Procedure Call)端口。(有关ALPC的更多信息,参见第3章。)该端口的名称采用console-PID-lpc-handle的格式,其中PID是Conhost进程的进程ID。然后,它向用户应用程序所关联的内核进程结构注册它的PID,因而用户应用程序可以查询此信息,以便打开新创建的ALPC端口。该进程也会创建一个共享内存区对象的映射关系,从而在命令行应用程序和它的Conhost之间可以共享数据。最后,在会话0的BaseNamedObjects目录中创建一个等待事件(名为ConsoleEvent-PID),从而使命令行应用程序和Conhost可以相互之间通知新的缓冲区数据。下面的图显示了一个Conhosti进程有两个句柄已经打开了此ALPC端口和等待事件。

图片.png

因为Conhost是用用户的凭证来运行的(意味着用户的特权级别),并且也运行在一个与控制台应用程序相关联的进程中,所以,用户界面特权隔离(UIPI,User Interface Privilege Isolation参见第6章“安全性”)安全机制也适用于控制台进程。而且,CPU制约的控制台应用程序可能会被识别为它们背后的控制台宿主进程(若有必要,用户可以杀掉它)。另外还有一种副作用,因为Conhost进程现在运行在Csrss子系统的特别辖区以外,所以,控制台应用程序(它们的窗口实际上属于Conhost)可以完全主题化,可以加载第三方DLL,还可以拥有完全的窗口能力。


UNIX应用子系统


针对UNIX应用程序的子系统(SUA,Subsystem for UNIX-based Application)使得可以在一台运行Windows Server系统,或者Windows客户机系统的企业或旗舰版本的计算机上编译和运行UNIX应用程序。SUA提供了将近2000个UNX函数和300个UNIX类的工具和实用程序。


(有关SUA的更多信息,参见 )


有关Windows如何处理POSIX应用程序运行的更多信息,参见第5章“CreateProcess的流程”一节。


最初的POSIX子系统


POSIX差不多可以看成“a Portable Operating System Interface based on UNIX(一个基于UNX的可移植的操作系统接口)”的缩写,它指的是针对UNX风格的操作系统接口的一组国际标准。POSX标准鼓励厂商实现UNX风格的接口,从而使它们保持兼容,这样程序员们就可以很容易地将他们的应用程序从一个系统迁移到另一个系统上。Windows最初仅仅实现了众多POSIX标准中的一个,即POSIX.1,正式的名称是ISO/IEC 9945-1:1990或者IEEE POSIX标准1003.1-1990.该标准之所以被包含进来,主要是为了满足美国*在20世纪80年代中后期制定的*采购要求,即POSX.1兼容性是强制性的,这是在美国标准和技术委员会(National Institute of Standards and Technology)开发的FIPS(Federal Information Processing Standard,联邦信息处理标准)l5l-2中规定的。Windows NT3.5、3.51和4已经被正式测试过,并且通过了FPS151-2的鉴定。


因为POSX.l兼容性是Windows的一个强制性目标,所以,在设计Windows操作系统时,必须要保证所设计的基本系统能够支持POSX.1子系统一一比如fork函数是在Windows执行体中实现的,而针对文件硬链接(hard file link)的支持则在Windows文件系统中。


Ntdll.dll


Ntdll.dll是一个特殊的系统支持库,主要用于子系统DLL。它包含两种类型的函数:


   系统服务分发存根(stub),它们会调用Windows执行体的系统服务。

   内部支持函数,供子系统、子系统DLL以及其他的原生映像文件使用。


第一组函数为Windows执行体系统服务提供了接口,在用户模式下可以通过这些接口函数调用Windows执行体的系统服务。这样的函数超过了400个,比如NtCreateFile、NtSetEvent等。


如前所述,这些函数的大多数功能可以通过Windows API来访问得到(然而,有些函数则不然,它们仅被用于操作系统内部)。

对于每一个这样的函数,Ntl包含了一个同名的入口点。函数内部的代码包含了与处理器体系架构相关的模式切换指令,通过该指令可转换到内核模式下,从而调用系统服务分发器(system service dispatcher,第3章将详细介绍)。系统服务分发器在检验某些参数以后,再调用真正的内核模式系统服务,其中包括Ntoskrnl.exe内部的实际代码。


对于每一个这样的函数,Ntdll包含了一个同名的入口点。函数内部的代码包含了与处理器体系架构相关的模式切换指令,通过该指令可转换到内核模式下,从而调用系统服务分发器(system service dispatcher,第3章将详细介绍)。系统服务分发器在检验某些参数以后,再调用真正的内核模式系统服务,其中包括Ntoskrnl.exe内部的实际代码。


Ntdll也包含许多支持函数,比如:


   映像加载器(以Ldr开头的函数)、

   堆管理器、

   Windows子系统进程通信函数(以Cs开头的函数)。


Ntdll也包含一般性的运行库例程(以Rtl开头的函数)、对用户模式调试和Windows事件跟踪的支持函数(分别以DbgUi和Ew开头的函数),以及用户模式异步过程调用(APC,Asynchronous Procedure Call)分发器和异常分发器(关于APC和异常,将在第3章中讲述)。最后,你还可以在Nt中发现一个很小的C运行库(CRT)例程的子集,仅

限于字符串和标准库中的一些例程,比如nemcpy、strcpy、itoa,等等。


执行体


Windows执行体是Ntoskrnl…exe中的上层(内核是其下层)。执行体包含以下类型的函数:


   可在用户模式下调用的导出函数。这些函数称为系统服务(system service),并且通过Ntdl被导出。这些服务绝大多数可通过Windows API来访问,或者通过另一个环境子系统的API来访问。然而对于有些服务,通过任何一个文档化的子系统函数都无法访问(这样的例子有ALPC、诸如NtQueryInformationProcess之类的各种查询函数,以及诸如NtCreatePagingFilei这样的专用函数,等等)。

   可通过DeviceloControli函数来调用的设备驱动程序函数。这为从用户模式到内核模式提供了一个通用的接口,因而在用户模式下可以调用设备驱动程序中并不与读或者写操作关联的函数。

   只能在内核模式下调用的导出函数,这些函数在WDK中已经文档化。

   在内核模式下调用,但未在WDK中文档化的导出函数(比如以Inbv开头的、在引导视频驱动程序中调用的函数)。

   定义为全局符号但是未被导出的函数。这包括在Ntoskrnl内部调用的支持函数,比如以Iop或者M开头的那些函数(分别是I/O管理器内部支持函数和内存管理内部支持函数)。

   未定义为全局符号,而是在一个模块内部的函数。


执行体包含了以下的主要组件(在本书后续章节中会详细地介绍):


   配置管理器(configuration manager,在第4章中介绍)负责实现和管理系统的注册表。

   进程管理器(process manager,在第5章中介绍)创建和终止进程和线程。针对进程和线程的底层支持是在Windows内核中实现的;而执行体则在这些低层对象的基础上又加上了额外的语义和功能。

   安全引用监视器(security reference manager,SRM,在第6章中讲述)强制在本地计算机上实行安全策略。它守护着操作系统的资源,执行对运行时对象的保护和审计。

   I/O管理器(I/O manager,在本书下册第8章中解释)实现了与设备无关的/O功能,负责将/O请求分派到恰当的设备驱动程序以进一步处理。

   即插即用(PnP)管理器(Plug and Play manager,在本书下册第8章中解释) 的任务是,为了支持某个特定的设备,确定哪些驱动程序是必需的,同时也负责加载这些驱动程序。它在设备枚举过程中,获取到每个设备的硬件资源需求。PP管理器根据每个设备的资源需求,分配适当的硬件资源,比如I/O端口、RQ、DMA通道和内存位置。当系统中的设备变化(增加或者移除设备)时,它还负责发送恰当的事件通知。

   电源管理器(power manager,在本书下册第8章中解释)负责协调电源事件,并且产生电源管理/O通知,发送给设备驱动程序。电源管理器可以配置成:当系统空闲时,通过将CPU置于睡眠状态而降低电源消耗。单个设备的电源消耗变化可由设备驱动程序来处理,但是需要电源管理器来协调。

   Windows驱动程序模型(Windows Driver Model)的WMI例程(在第4章中解释)允许设备驱动程序发布有关性能和配置的信息,以及接收来自用户模式WMI服务的命令。WMI信息的消费者可以运行在本地机器上,也可以在跨网络的远程机器上。

   缓存管理器(cache manager,在本书下册第1l章中解释)提高了基于文件的I/O操作的性能,其做法是,让最近引用过的磁盘数据驻留在主内存中以便快速访问(并且延迟磁盘写操作,在将更新数据发送到磁盘之前先在内存中停留一小段时间)。你将会看到,它利用了内存管理器对映射文件的支持来做到这一点。

   内存管理器(memory manager,,在本书下册第10章中解释)实现了虚拟内存。这是一种内存管理方案,它为每个进程提供一个巨大的私有地址空间,其大小可以超过当前可用的物理内存。内存管理器也为缓存管理器提供相应的底层支持。

   逻辑预取器(logical prefetcher)和Superfetch(在本书下册第l0章中解释)用于加速系统和进程的启动过程。其做法是,对于要在系统或进程启动过程中引用的数据,它优化这些数据的加载过程。


另外,Windows执行体还包含四组主要的支持函数,以上这些执行体组件会用到这些支持函数。在这些支持函数中,差不多三分之一在WDK中有相应的文档,因为设备驱动程序也要用到它们。以下就是这四大类支持函数:


   对象管理器(object manager),创建、管理和删除Windows执行体对象和抽象数据类型,它们代表了操作系统的资源,比如进程、线程和各种同步对象。第3章将介绍对象管理器。

   高级LPC设施(ALPC facility,在第3章中解释),为同一台机器上的客户机进程和服务器进程传递消息。此外,ALPC也被用作RPC的一个本地传输实现,这里的RPC是指跨网络的客户机进程和服务器进程之间的工业标准通信设施。

   一组涉及范围广泛的公共运行库函数,比如字符串处理、算术操作、数据类型转换,以及安全结构处理等。

   执行体支持例程,比如系统内存分配(换页的和非换页的内存池)、互锁的内存访问,以及三种特殊类型的同步对象:资源、快速互斥体(fast mutex)和推锁(pushlock)。


执行体还包含了其他多种基础设施例程,在本书后面我们将只是简单地提及其中一部分:


   内核调试器库(kernel debugger library)使得内核的调试与支持KD的调试器保持独立,这里KD是指一个可移植的协议,可以承载在各种传输体(比如USB和EEE1394)上。WinDbg和Kd.exe工具实现了KD协议。

   用户模式调试框架(user-mode debugging framework)负责向用户模式调试API发送事件,支持断点和单步跟踪代码,以及改变运行线程的执行环境。

   内核事务管理器(kernel transaction manager)提供公共的两阶段提交机制供资源管理器(resource manager)。使用,比如事务型注册表(TxR,transactional registry)和事务型NTFS(TxF,transactional NTFS)。

   超级管理器库(hypervisor library)是Windows Server2008中Hyper-V栈的一部分,它提供了虚拟机环境的内核支持。当系统知道它在一个客户区(虚拟环境)中运行时,超级管理器库可以优化相应的内核代码。

   错误修正管理器(errata manager)为非标准的或非兼容的硬件设备提供绕行的解决方案。

   驱动程序检验器(Driver Verifier)为内核模式驱动程序和代码提供可选的一致性检查机制。

   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编写的,对于那些要用到特殊的处理器指令和寄存器(不容易在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,也会显示出来。

图片.png

图片.png

可以使用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.系统”的问题便不复存在。

图片.png

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


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

上一篇: 总线驱动器 - SPI 驱动器(顶部)

下一篇: Linux nvme 驱动程序详细信息