引言

启动过程是我们了解操作系统的第一个环节。了解 Windows 的启动过程,可以帮助我们解决一些启动的问题,也能帮助我们了解 Windows 的整体结构。

以下内容将分为【加载内核】、【内核初始化】和【应用程序初始化】三个部分。

加载内核

启动过程概览 所示,加载过程分为两种方式。传统的 BIOS(Basic Input/Output System)和 UEFI(Unified Extensible Firmware Interface)。这两种方式大致相似,本文以 BIOS 为例来说明引导说过。

加载 BIOS

此部分参考:How BIOS Works (yale.edu)

当开机键被按下后,电源将执行初始化,确保电源可靠后,主板上的硬件电路给 CPU 发复位(reset)信号。

CPU 收到复位信号后,将从 RAM 中读取指令执行。此时内存中是没有任何指令需要执行的。于是 CPU 就去 BIOS ROM 中特定的位置(FFFF0H)中执行指令,这个指令是一个跳转指令 ,将告诉 CPU 真正的 BIOS 程序所在的位置。跳转指令如下:

jmp far f000:e05b ; 跳转到0xfe05b执行

运行 BIOS

BIOS 主要包含以下四个部分的程序:

  • POSTPower On Self Test。用于检测硬件的可用性,如果有问题,会发出蜂鸣声并且停止进行下一步。
  • Bootstrap Loader:用来确定操作系统的位置,帮助引导操作系统的启动。此程序会遍历所有的存储设备的0盘0道1扇区的内容,如果这一扇区最后两字节的内容是0x55 0xaa,则认为它是启动区(也就是 MBR,Master Boot Record),并将扇区的内容(也就是ntldr)复制到内存0x7c00位置。至此,代码的控制权就交给了操作系统代码了。

Windows 的做法是,让引导扇区中的代码读入其他扇区的数据,然后跳转到下一个扇区的代码区。这样就可以不受单个引导扇区长度的限制,这种做法相当于将第一个引导扇区当做一个加载器(loader),而真正完成引导扇区功能的扇区随后被加载进来并执行。这一过程对于 MBR 是透明的,从而保持良好的兼容性。

  • BIOS:用于软件和硬件的通信。
  • CMOS Setup:一个配置程序,用于配置操作系统的时间、日期和密码等

加载 NT 内核

此时代码的控制权已经来到了 ntldr(也可以是 WinLoad 或者 os loader),此时处理器还运行实模式下,所以 ntldr 也分为两部分:实模式代码和保护模式代码。

在实模式下,ntldr 的主要任务是:

  • 完成在实模式下执行的初始化。比如清除键盘缓冲区。
  • 为切换到保护模式做好基本的环境准备。
  • 将处理器切换到保护模式下,然后将控制权交给保护模式下的代码。

在保护模式下,需要完成以下步骤:

  1. 由于虚拟机制转译机制还未就绪,首先要做的就是把物理内存管理起来。采用一个内存描述符数组将每一段内存的大小和用途记录下来,然后打开页面映射机制
  2. 继续执行其他的初始化工作。包含 I/O 设备的初始化等
  3. 从系统分区的根目录下读取 boot.ini 文件。并检查是否存在有效的 hiberfil.sys 文件,如果存在,则将引导过程转移到休眠系统的恢复过程。否则,将解析 boot.ini 文件 。
  4. 运行 NTDETECT.COM 程序。此程序运行在实模式下,它将利用 BIOS 来查询系统的基本设备和配置信息,并收集起来。在引导后期,将其存放到注册表 HKLM\HARDWARE\DESCRIPTION 下。
  5. 加载 ntoskrnl.exe、hal(默认为 hal.dll,具体是哪个映像文件,在 boot.ini 中会有记录),再通过加载注册表的 system 储巢,即 WINDOWS\system32\config\system 文件 拿到哪些设备驱动程序必须被加载进来,然后再把必要的设备驱动加载进来。
  6. 此时加载的准备工作基本就绪,将准备的信息构造成一个参数块 LOADER_PARAMETER_BLOCK.如下定义来自 WRK。
typedef struct _LOADER_PARAMETER_BLOCK {
LIST_ENTRY LoadOrderListHead; // 加载的模块链表,每个元素都为 KLDR_DATA_TABLE_ENTRY
LIST_ENTRY MemoryDescriptorListHead; // 内存描述符链表,每个元素都为 MEMORY_ALLOCATION_DESCRIPTOR
LIST_ENTRY BootDriverListHead;// 引导驱动程序链表,每个元素都为 BOOT_DRIVER_LIST_ENTRY
ULONG_PTR KernelStack;// 内核栈顶
ULONG_PTR Prcb;// 进程环境,指向一个进程控制块
ULONG_PTR Process;// 初始进程,EPROCESS
ULONG_PTR Thread;// 初始线程,ETHREAD
ULONG RegistryLength;// System 储巢的长度
PVOID RegistryBase;// System 储巢的基地址
PCONFIGURATION_COMPONENT_DATA ConfigurationRoot;// 配置树,包含 ISA、磁盘和 ACPI 的配置数据
PCHAR ArcBootDeviceName;// 引导分区的 ARC 名称
PCHAR ArcHalDeviceName;// 系统分区的 ARC 名称
PCHAR NtBootPathName;// OS 目录的路径名称,比如“\Windows”
PCHAR NtHalPathName;// OS 加载器的路径名称,比如“\”
PCHAR LoadOptions;// 引导选项,来自 boot.ini
PNLS_DATA_BLOCK NlsData;// 包含 ANSI 代码页、OEM 代码页和 Unicode 码表
PARC_DISK_INFORMATION ArcDiskInformation;// 所有磁盘的签名结构
PVOID OemFontFile;// OEM 字体文件
struct _SETUP_LOADER_BLOCK *SetupLoaderBlock;// 网络引导或文字模式安装引导
PLOADER_PARAMETER_EXTENSION Extension;// 扩展部分 union {
I386_LOADER_BLOCK I386;
// ALPHA_LOADER_BLOCK Alpha;
// IA64_LOADER_BLOCK Ia64;
} u;
} LOADER_PARAMETER_BLOCK, *PLOADER_PARAMETER_BLOCK;
  1. 最后,ntlrd 将控制权交给 ntoskrnl.exe 的入口函数。表示内核加载完成。

内核初始化

到这里,接下来的过程,我们就可以通过 WRK 来观察具体的过程了。

ntoskrnl.exe 的入口函数是 _KiSystemStartup,可以顺着这个方法往下看。

整个初始化分为两个阶段:Phase0 和 Phase1。

阶段1的初始化入口是 Phase1InitializationDiscard 方法。此方法也是做了各种初始化,具体可以去看代码。由于其时间比较长,所以内部做了一个进度估计。


待 阶段1 完成初始化,执行体的各个组件都进入了一个正常状态。但只有内核是没有意义的,系统必须让应用程序跑起来。应用程序的启动就得依赖刚启动的 smss 进程继续了。

应用初始化

现在控制权来到了 smss,smss 是 Windows 中一个重要的进程。

smss 还会继续完成引导过程。在内核初始化期间,只有 System 储巢被加载到内存当中了。其他的就得由 smss 来加载。其他初始化必要的工作,可以参看 HKLM\SYSTEM\CurrentControlSet\Control\Session Manager 。

注册表中能发现一些有趣的子键。

  • 建立系统的全局环境变量,这些环境变量由 Environment 键下的值指定
  • 运行在启动时执行的程序,这些程序由 BootExecute 值指定
  • 执行启动时的文件删除或重命名任务,这由 FileRenameOperations 子键来指定
  • 启动 Windows 子系统进程(csrss.exe)。子系统进程的命令行字符串由 SubSystems 子键的 Windows 值指定

然后 smss 便会启动 winlogon 进程,随后接下来的引导便交由 winlogon 进程了。 winlogon 主要职责如下:

  1. 创建初始窗口
  2. 创建登录桌面和默认桌面
  3. 启动服务控制管理器(SCM,Service Control Manager)进程(services.exe)
  4. 启动本地安全权威子系统(lsass)进程

在登录过程的最后,winlogon 检查注册表 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Userinit 的值,并创建一个进程来运行该值字符串。该值串的默认值为 userinit.exe 程序的路径。Userinit 进程加载当前登录用户的轮廓,然后检查 HKCU\Software\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,并创建一个进程来运行该值字符串;如果该值不存在,则运行 HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Shell 的值,其默认值为 explorer.exe。然后,userinit 进程退出。由于当前登录会话的 Shell 程序(explorer.exe)已经启动,因此用户可以在桌面上操作了。


至此,引导就全部结束了。

Windows 启动过程的更多相关文章

  1. UEFI下windows启动过程

    引导文件 在UEFI安装完操作系统后,Windows至少使用两个分区,一个叫做ESP分区(EFI SYSTEM PARTITION),用于存放启动文件,另一个则是BIOS下正常的系统分区,不同的是,B ...

  2. Windows启动过程(MBR引导过程分析)

    catalogue . 电脑启动过程 . MBR分析(master boot record) - 位于整个硬盘的 扇区 . DBR(DOS boot record) - 位于柱面0,磁头1,扇区1,即 ...

  3. Windows Phone-框架结构和启动过程

    上一篇文章介绍了Windows Phone的开发环境和一个简单的Windows Phone程序的演示和结构,这一篇文章要深入一点,介绍Windows Phone的框架结构和程序启动的过程. 一 Win ...

  4. [oracle] oracle的三种密码验证机制以及在windows和linux下的不同启动过程

    oracle数据库的密码验证机制: ① 操作系统验证 拥有SYSDBA和SYSOPER的用户用该方式验证此时数据库无需启动,也无需开启监听和实例服务. 要求:本地组ora_dba中有该操作系统的登录用 ...

  5. Windows平台下Oracle实例启动过程中日志输出

    Windows平台下Oracle实例启动过程中日志输出记录. 路径:D:\app\Administrator\diag\rdbms\orcl\orcl\trace\alert_orcl.log 输出内 ...

  6. Windows平台下Oracle监听服务启动过程中日志输出

    Windows平台下Oracle监听服务启动过程中日志输出记录. 日志目录:D:\app\Administrator\diag\tnslsnr\WIN-RU03CB21QGA\listener\tra ...

  7. 探索 Linux 系统的启动过程

    引言 之所以想到写这些东西,那是因为我确实想让大家也和我一样,把 Linux 桌面系统打造成真真正正日常使用的工具,而不是安装之后试用几把再删掉.我是真的在日常生活和工作中都使用 Linux,比如在 ...

  8. linux内核学习之三 跟踪分析内核的启动过程

    一   前期准备工作       1 搭建环境 1.1下载内核源代码并编译内核 创建目录,并进入该目录: 下载源码: 解压缩,并进入该目录:xz -d linux-3.18.6.tar.xz tar ...

  9. OGRE启动过程详解(OGRE HelloWorld程序原理解析)

    本文介绍 OGRE 3D 1.9 程序的启动过程,即从程序启动到3D图形呈现,背后有哪些OGRE相关的代码被执行.会涉及的OGRE类包括: Root RenderSystem RenderWindow ...

随机推荐

  1. git冲突解决、线上分支合并、luffy项目后台登陆注册页面分析引入

    今日内容概要 git冲突解决 线上分支合并 登陆注册页面(引入) 手机号是否存在接口 腾讯云短信申请 内容详细 1.git冲突解决 1.1 多人在同一分支开发,出现冲突 # 先将前端项目也做上传到 g ...

  2. 携程开源分布式配置系统Apollo服务端是如何实时更新配置的?

    引言 前面有写过一篇<分布式配置中心apollo是如何实时感知配置被修改>,也就是客户端client是如何知道配置被修改了,有不少读者私信我你既然说了client端是如何感知的,那服务端又 ...

  3. XCTF练习题---MISC---Erik-Baleog-and-Olaf

    XCTF练习题---MISC---Erik-Baleog-and-Olaf flag:flag{#justdiffit} 解题步骤: 1.观察题目,下载附件 2.拿到手以后发现是一个没有后缀名的文件, ...

  4. C++基础-2-引用

    2. 引用 2.1 引用的基本语法 1 #include<iostream> 2 using namespace std; 3 4 5 int main() { 6 7 //引用的基本语法 ...

  5. Go 项目配置文件的定义和读取

    前言 我们在写应用时,基本都会用到配置文件,从各种 shell 到 nginx 等,都有自己的配置文件.虽然这没有太多难度,但是配置项一般相对比较繁杂,解析.校验也会比较麻烦.本文就给大家讲讲我们是怎 ...

  6. JS 的 new 是个啥?

    JS 的 new 是个啥? 本文写于 2019 年 11 月 25 日 new关键字在很多语言里面,总是用于把类实例化,可是 JS 之前就没有"类"这个概念呀. 那 JS 的new ...

  7. python 动态规划(背包问题和最长公共子串)

    背包问题 现在要往一个可以装4个单位重量的背包里怎么装价值最高:A重量1个单位,价值15:B重量3个单位,价值20:C重量4个重量,价值30 使用动态规划填充空格 class SolutionBag: ...

  8. 将MySQL查询结果导出到Excel

    总结将mysql的查询结果导出到文件的方法 总结 使用命令 select user, host, password from mysql.user into outfile '/tmp/user.xl ...

  9. python之部分内置函数与迭代器与异常处理

    目录 常见内置函数(部分) 可迭代对象 迭代器对象 for循环内部原理 异常处理 异常信息的组成部分 异常的分类 异常处理实操 异常处理的其他操作 for循环本质 迭代取值与索引取值的区别 常见内置函 ...

  10. README.exe 是的,你看错是EXE

    SmartIDE让你的README变成可执行文档,再也不用编写无用的文档,再也不必操心环境问题.   作为开发者,拿到一个新的代码库的时候一般都会先去看README文件,通过这个文件可以知道这套代码所 ...