发现windows internals的第二卷还没有出中文版,所以自己边看边译
第13章 开机和关机
在这一章中,我们将解释启动windows所需的步骤和可以影响系统启动的选项,理解启动过程的细节将会帮助你定位在引导时可能出现的问题。然后我们将解释在引导过程中可能出错的事情的类型和如何解决这些问题。最终地,我们将解释在正常系统关机时将会发生什么。
引导过程
在解释windows引导过程时,我们将从windows的安装和引导支持文件执行的处理过程开始。设备驱动是启动过程的一个重要的部分,因此我们将解释在引导过程中它们的控制点的方式和它们如何加载和初始化。
接着我们将解释执行子系统是如何初始化的和内核如何通过启动会话管理器进程(smss.exe)来加载用户模式的部分,如何启动初始的两个会话(会话0和会话1)。沿着这条路径,我们将突出重点于不同的屏幕消息出现以帮助你将你看到的windows启动过程和内部的过程相关联。
引导过程的早期阶段在不同的使用BIOS的系统和EFI的系统之间是有相当的不同的。EFI是一个新的标准与BIOS系统的16兼容代码模式相距甚远,允许加载预引导程序和驱动以支持操作系统的加载过程。下一节秒速特定于基于BIOS的系统的引导过程部分,紧接着的一节描述特定于EFI的引导过程的部分。
为了支持这些不同的硬件实现(包括EFI2.0,被认为是统一EFI或UEFI)。为了提供一个一致的环境和与系统安装的硬件无关的体验,windows 提供了一个启动架构抽象化了许多用户和开发者之间的差异。
BIOS预引导
windows引导过程并不在你按下电源按钮或者按下重置按钮时开始。它从你安装windows到你的电脑时开始。在windows 安装程序执行的某些点,系统的主硬盘驱动器就准备好了启动工程的代码。在我们深入这些代码之前,让我们看看windows将这些代码放在哪里和如何放置的。在早期的MS-DOS时代,一个物理硬盘分割为卷的标准就已经在x86系统中存在了。 微软的操作系统将硬盘分割为离散的区域,并认为是分区,使用文件系统(例如FAT和NTFS)来格式化每一个分区为一个卷。一个硬盘可以包含4个主分区。 因为这个分配模式将会把一个磁盘限制为4个卷,一个特殊的分区类型,叫做一个扩展分区,进一步将每一个扩展分区分配为四个附加分区。扩展分区也可以包含扩展分区,还可以再包含扩展分区,等等。这使得一个操作系统放置于一个磁盘的卷的个数实际上是无限的。图表13-1给出了一个硬盘的结构的例子。表格13-1总结了BIOS引导过程中包含的文件。(你可以在第九章“存储管理”中学到更多关于windows分区的知识)
表格13-1 BIOS引导过程的部件
部件 | 处理器执行 | 职责 | 位置 |
主引导记录(MBR) | 16位实模式 | 读取和加载卷引导记录VBR | 每一个存储设配
|
引导扇区(也叫做卷引导记录) | 16位实模式 | 获知分区上的文件系统和通过名字定位引导管理器,把它加载到内存 | 每一个活动的可引导分区 |
引导管理器 | 16位实模式和32位未分页模式 | 读取引导配置数据库,当前的引导菜单,允许执行预引导程序,如内存测试程序(memtest.exe) 如果是一个64位的安装被引导,则在加载Winload之前切换到64为长模式 | 每一个系统 |
winload.exe | 32位保护模式(已分页),如果是64位安装的话64位保护模式 | 加载ntoskrnl.exe和它的依赖库(bootvid.dll 位于32 位系统,hal.dll,kdcom.dll,ci.dll,dlfs.sys,pshed.dll)和引导启动的设备驱动 | 每一个windows安装 |
winresume.exe | 32位保护模式,如果是唤醒一个64位安装的话64位保护模式 | 如果在一个睡眠状态后唤醒,从睡眠文件中唤醒(Hiberfil.sys)而不是典型的windows加载 | 每一个windows安装 |
memtest.exe | 32位保护模式 | 如果从引导管理器选择,启动并且提供一个扫描内存和检测受损RAM的图形接口 | 每一个系统 |
ntoskrnl.exe | 分页的保护模式 | 初始化执行子系统和引导系统启动设备驱动,为运行native application准备系统,运行smss.exe | 每一个windows安装 |
Hal.dll | 分页的保护模式 | ntoskrnl和驱动程序与硬件之间的接口的内核模式动态库。它也作为主板自己的一个驱动,支持不受其它驱动管理的连接部件。 | 每个windows安装 |
smss.exe | 本地应用程序 | 初始化实例并且将自身拷贝到每一个会话以完成初始化。会话0实例加载 windows子系统驱动win32k.sys并且启动 windows子系统进程(csrss.exe)和windows初始化进程(wininit.exe)。所有其它的会话实例开始于一个csrss和winlogon进程 | 每个windows 安装 |
wininit.exe | windows应用程序 | 启动服务控制管理器(SCM), 本地安全认证进程(LSASS)和本地会话管理器(LSM),初始化剩下的注册表和完成用户模式初始化任务 | 每个windows安装 |
winlogon.exe | windows应用程序 | 协调登录和用户安全,加载登录UI | 每个windows 安装 |
logonui.exe | windows应用程序 | 显示交互式登录对话框 | 每个winsows安装 |
services.exe | windows应用程序 | 加载和初始化自动启动设备驱动和windows服务 | 每个windows安装 |
物理磁盘以我们所知的扇区来寻址。在一个BIOS PC中一个硬盘扇区典型为512个字节(但是可以直到4096字节,查看第九章以获得更多信息),工具集为了定义卷而准备硬盘,例如windows设置程序,写入一个扇区的数据,叫做主引导记录,到一个磁盘的第一个扇区。(MBR分区在第九章描述)MBR包含一个固定数量的空间,包含了可执行指令(叫做引导代码)和一个表格(叫做分区表),包含四个入口,定义了磁盘上的主分区的位置。当一个基于BIOS的计算机引导时,首先执行的代码叫做BIOS,已经编码入计算机的闪存。BIOS选择一个启动设备,读取设备的 MBR到内存,并将控制权移交给MBR中的代码。
操作系统通常在没有用户介入的情况下写入引导扇区。例如,当windows安装写入MBR到一个硬盘时,它也写入文件系统引导代码(引导扇区的一部分)到一个磁盘上的100MB的可引导分区,并标记为隐藏的,以避免在操作系统加载后意外的修改。这是我们前面解释过的系统卷。
在写入一个分区的引导扇区之前,windows安装先确认引导分区(引导分区是windows安装的分区,通常与存放引导文件的系统分区不同)格式化为NTFS,唯一支持windows安装到一个硬盘后可以引导的文件系统,或者将引导分区(和其它任何分区)格式化为NTFS。
注意系统分区的格式可以使windows支持的任何文件系统(例如fat32)。如果分区已经正确地格式化,你可以指导安装程序忽略这一步。当安装程序格式化系统分区之后,安装程序拷贝引导管理程序(Bootmgr)到windows使用的系统分区(系统卷)
另一个安装的任务是预备引导配置数据库(BCD),在BIOS系统中存储于系统卷的根目录的\Boot\BCD 文件中。这个文件包含安装程序安装的windows版本的启动选项和其它任何已安装的windows。如果BCD 已经存在,安装程序简单地添加新的与新安装相关的条目。若要查看BCD相关的更多信息,可以查看第一部分的第三章,"系统机制"
BIOS引导扇区和引导管理器
安装程序在写入一个引导扇区前必须首先知道分区的格式,因为引导扇区的内容依赖于分区格式而不同。在一个NTFS格式的分区中,windows写入NTFS兼容的代码。引导扇区代码的主要任务是给予windows有关卷的结构和格式的信息和从卷的根目录读取引导管理文件。通常地,引导扇区代码包含一个足够的只读文件系统代码已完成这一任务。在引导扇区代码加载引导管理器到内存后,它将控制权移交给引导管理器的入口点。如果引导扇区代码不能在卷的根目录找到引导管理器,它将显示错误信息"BOOTMGR is missing"
启动管理器实际上是一个.com文件(startup.com)和一个.exe文件(bootmgr.exe)的串联,因此它从系统的称为实模式的x86操作模式开始存在,与.com文件关联。在实模式下,没有内存地址的虚拟地址到物理地址的转换,这意味着程序使用的内存地址被解释为物理地址并且只有计算机物理内存的前1M才可访问。简单的MS_DOS程序在实模式环境中执行。然而,启动管理器的第一个动作是切换系统到保护模式。在引导过程的这一点上仍然没有虚拟地址到物理地址的转换,但是完整的32位物理内存变得可以访问。在系统进入保护模式之后,启动管理器可以方位所有的物理内存。在创建完足够的的页表使得小于16M的内存在分页启用后可以访问,启动管理器启动分页。带分页的保护模式是windows执行的通常操作的模式.
在启动管理器启用保护模式后,它就成为完整可操作的。然而它仍然依赖于BIOS提供的函数来访问基于IDE的系统和启动磁盘,如所显示的一样。启动管理器的BIOS接口函数短暂地切换处理器到实模式,使得BIOS提供的服务可以被执行。启动管理器下一步使用内置的文件系统代码从\Boot目录读取BCD文件。如引导扇区的代码一样,启动管理器也包含一个轻量级的NTFS文件系统库(启动管理器也支持其它文件系统,如FAT,EI Torito CDFS,和UDFS,还包括WIM和VHD 文件);不像引导扇区的代码,启动管理器的文件系统代码可以读取子目录。
注意启动管理器和其它的引导程序也可以写入NTFS卷中预分配的文件,因为只有数据需要被写入,而不是完成所有复杂的特别是NTFS卷所需的分配工作。这就是为什么这些应用程序可以写入如bootsect.dat文件
启动管理器下来会清理屏幕。如果windows使能BCD设置以通知启动管理器从睡眠中恢复,这将是通过加载winresume.exe实现的捷径,它将读取睡眠文件的内容到内存并且将控制权移交给内核中恢复一个已睡眠的系统的代码。该代码负责将在关闭系统时活跃的驱动激活。Hiberfil.sys只有在最后一次计算机关机是睡眠时才有效,在一个唤醒之后睡眠文件变为无效,以避免从一个点上多次唤醒。(参考第八章的"电源管理"节,"I/O系统"来获得更多的有关睡眠的信息)
如果在BCD中有多于一个引导选项,启动管理器则会给用户显示引导选择菜单(如果只有一个入口,启动管理器将会忽略该菜单并继续加载winload.exe)在BCD的选项入口指引启动管理器哪一个分区是所选的安装存在的windows系统目录(典型为\Windows)。如果windows是从一个旧的版本更新,这个分区可能与系统分区相同,或者,在一个干净的安装下,它将始终位于我们前面所述的100MB 的隐藏分区
BCD的入口可以包含可选的参数,传递给启动管理器、winload,和其他启动相关的部件。表格13-2包含一个这些选项的列表和它们对启动管理器的影响,表格13-3列出了BCD用于引导应用程序的一个列表,表格13-4给出了BCD对windows引导加载器的选项
Bcdedit.exe工具提供了一个设置一些开关的便捷的接口。一些包含在BCD的选项存储为命令行开关(例如"/DEBUG")保存在注册表项HKLM\SYSTEM\CurrentControlSet\Control\SystemStartOptions;否则,它们仅仅保存在BCD巢的BCD二进制格式中。
表13-2与windows启动管理器相关的BCD选项
BCD元素 | 值 | 含义 |
Bcdfilepath | Path | 指向磁盘上的启动配置数据库(通常是\Boot\BCD)文件 |
Displaybootmenu | Boolean | 决定启动管理器是否显示引导菜单或者自动选择默认的入口 |
Keyringaddress | Physical address | 指定保存BitLocker关键环的物理地址 |
Noerrordispaly | Boolean | 使得启动管理器遇到的错误输出无效 |
Resum | Boolean | 指定是否从检测到的睡眠文件中唤醒。这个选项是在windows睡眠时自动设置的 |
Timeout | Seconds | 启动管理器在选择默认入口之前等待的秒数 |
Resumeobject | GUID | 标识在系统睡眠后将使用哪一个引导程序唤醒系统 |
Displayorder | List | 定义启动管理器的显示顺序列表 |
Toolsdisplayorder | List | 定义启动管理器的工具显示顺序列表 |
Bootsequence | List | 定义一次性的引导顺序 |
Default | GUID | 默认加载的引导入口 |
Customations | List | 定义当一个特定的键盘序列输入时产生的定制的行为 |
Bcddevice | GUID | BCD存储所在的设备ID |
表格13-3 BCD为引导应用程序提供的选项
BCD元素 | 值 | 含义 |
Avoidlowmemory | Integer | 强制物理地址低于特定的值以使引导加载器尽可能地避免访问。某些时候legacy设备(例如ISA)需要使得低于16M的内存为可用或不可见 |
badmemoryaccess | Array of page frame numbers(PFNs) | 指定一个系统中的物理页面的列表由于RAM错误而不可用 |
Baudrate | Baud rate in bps | 指定一个覆盖默认波特率(19200)的值,使得远程内核调试器主机可以通过一个串口连接 |
Bootdebug | Boolean | 对引导加载器使能远程引导调试。如果这个选项有效,你可以使用kd.exe或者windbg.exe连接到引导加载器 |
Bootems | Boolean | 用于通知windows对引导应用程序启用紧急任务管理服务,报告启动信息和通过串口接受系统管理命令 |
busparams | String | 如果一个物理PCI调试设备用于提供火线或串口调试,为设备指定 PCI总线,函数和设备代码 |
Channel | Channel between 0 and 62 | 用于与{调试类型,1394}连接,指定内核调试通信将会流经的IEEE1394信道 |
Configaccesspolicy | Default DisallowMmConfig | 配置系统是否使用内存映射I/O来访问PCI厂商的配置空间或者回退到使用HAL的IO端口访问例程。在某些时候对解决平台依赖的设备问题时很有用 |
Debugaddress | Hardware address | 指定串口(COM)端口的硬件地址用于调试 |
Debugport | COM port number | 指定一个覆盖默认串口(通常在最少有两个串口的系统中是COM2)使得远程的内核调试器可以连接得到 |
Debugstart | Active,AutoEnable,Disable | 在内核调试可用的情况下为调试器指定设置。AutoEable使得当一个断点或内核异常发生时调试器可用,包括内核崩溃发生 |
Debugtype | Serial ,1394,USB | 设置内核调试器是通过串口、火线(IEEE1394),或者USB2.0端口通信(默认是串口) |
Emsbaudrate | Baud rate in bps | 指定使用EMS的波特率 |
Emsport | COM port number | 指定EMS使用的串口(COM)端口 |
Extendedinput | Boolean | 允许引导应用程序影响BIOS对扩展控制台输入的支持 |
Firstmegabytepolicy | UseNone,UseAll,UsePrivate | 指定HAL如何使用低1M物理内存,以在电源转换时 减少BIOS崩溃 |
Fontpath | String | 指定引动应用程序使用的OEM字体的路径 |
Graphicsmodedisabled | Boolean | 为引导应用程序禁用图形模式 |
Graphicsresolution | Resolution | 为引导应用程序设置图形分辨率 |
Initialconsoleinput | Boolean | 指定一个初始化字符使得系统可以插入到PC/AT键盘的输入缓冲区中 |
Integrityservices | Default,Disable,Enable | 启用或禁用代码完整性服务,用于内核模式代码校验。默认是启用 |
Local | Localization String | 为引导应用程序设置区域,例如EN-US |
Noumex | Boolean | 在内核调试启用时禁用用户模式异常。如果你希望在以调试模式引导时挂起(冰冻),可以尝试启用这个选项 |
Novesa | Boolean | 禁止使用VESA显示模式 |