原文地址: manybutfinite.com/post/how-co… 注:文章发布于 2008.6.5,部分内容可能已经过时
为使阅读顺畅,在翻译的过程中进行了一些删改和补充。修改较小处不单独标出,只有较大量的补充会进行标注
前一篇博客讲解了 Intel 电脑的主板和内存映射,为本文讲解电脑启动的最初阶段进行了知识铺垫(所以建议在阅读上一篇译文后再来阅读本文)。
启动是一个复杂的、某些部分有些讨巧的、多阶段的有趣过程。下面这张图描述了大致的启动过程:
电脑的启动,是在我们按下电脑电源键的时候开始的(这是一句有必要说的废话)。主板一上电就会开始初始化自己的固件、芯片组还有其他的各种东西,然后会尝试让 CPU 开始运行。如果在这时出现问题,CPU 没能启动起来(比如 CPU 坏掉了或者没装 CPU ),电脑看起来就会像挂了一样 —— 某些主板会在发现 CPU 不存在或挂了的时候发出蜂鸣提醒用户,但就我的经验而言,最常见的情况还是只有风扇在转而没有任何其他反应。某些插在电脑上的 USB 设备或其他类型的设备也会造成这种情况的出现:如果电脑之前一直可以正常工作,只是某次开机时突然这样子起不来了,那么可以试着拔掉所有的非必要设备然后重启,这样就有可能能恢复正常。然后我们就可以用排除法来找到引起问题的设备。
如果在你按下电源按钮后一切顺利,那么 CPU 就会开始运行了,在一个有着多个 CPU 或多个 CPU 核心的电脑中,电脑会随机选一个核心作为启动处理器(BSP,Bootstrap Processor)来处理计算机启动过程中的各种事务。
其他剩下的处理器被称为应用处理器:AP, Application processor。AP 会保持待机(halt),直到稍后被内核逐个激活
Intel 的 CPU 每年都在进步,但一直是保持了完全的向后兼容性,所以现代 CPU 仍能够和 1978 年的 Intel 8086 在启动后有完全相同的表现 —— 在这个初始的上电过程中,CPU 是在实模式下工作的,内存分页此时也不可用:因此,早期的 MS-DOS 系统只能寻址 1MB 的内存,而且可以直接读写内存中的任何地址——那个时代还没有特权和保护的概念。
CPU 中绝大多数寄存器(en.wikipedia.org/wiki/Proces… )都会在上电后被初始化为已定义的值,包括指令指针寄存器(EIP, instruction pointer。该寄存器用于保存即将被 CPU 执行的下一条指令的内存地址)。Intel CPU 使用一个比较 hack 的做法,实现了在启动早期只能寻址 1MB 内存的情况下,仍可以用一个隐藏的基址(base address,还有一个必不可少的偏移量 offset )来赋给 EIP,从而让第一条指令可以在 0xFFFFFFF0(比 4GB 少 16Bytes,但远高于 1MB)。这个魔法般的地址被称为重置向量(reset vector en.wikipedia.org/wiki/Reset_… ),该功能已经是现代 Intel CPU 的标配特性之一。
主板确保重置向量处的指令是跳转到一个内存地址,该内存地址映射到 BIOS 入口点。该次跳转会隐式清除在上电时的隐藏基址地址。因为芯片组保存了内存映射信息,所以这些内存地址都指向了 CPU 在该阶段所需的正确的内容。这些内存都映射到了 BIOS 所在的闪存中,而此时 RAM 模块中还只是一堆没有用的随机数。相关的内存区域如下图所示
接下来,CPU 就要开始执行 BIOS 代码了,这些代码会初始化机器中的一些硬件,随后 BIOS 会开始进行开机检测( Power-on Self Test,POST ),在这个阶段 BIOS 会对电脑中的各种组件进行测试。例如,在 POST 由于缺少可以正常工作的显卡而失败的情况下, BIOS 会停止工作并通过发出蜂鸣的方式,提示用户遇到的问题 —— 因为这种情况下无法在屏幕上显示任何东西。在有可以正常工作的显卡的情况下,电脑就会进入到一个“看起来活过来了”的状态中:屏幕上显示出了厂商 logo ,开始进行内存测试。其他的 POST 失败情况下(例如没有键盘等),电脑则通常会在屏幕上显示一条错误信息来进行提示。
POST 的过程包括测试和初始化,会检测所有资源(中断、内存范围、PCI 设备的 I/O端口等)。遵循高级配置与电源接口(Advanced Configuration and Power Interface, en.wikipedia.org/wiki/ACPI)的现代 BIOS 会构建一些描述电脑中设备的数据表,这些数据表会在接下来提供给内核使用。
在 POST 之后,BIOS 就该启动操作系统了——被启动的操作系统应该存储在可以访问的某处,如硬盘、 CD-ROM 驱动器、软盘等。BIOS 实际的启动顺序是用户可以配置的。如果没有合适的启动设备(比如硬盘挂了),BIOS 就会停止工作并给出类似于 “Non-System Disk or Disk Error” 的提示。如果一切正常,BIOS 就能够找到一个可以工作的磁盘并开始启动过程。
首先, BIOS 会读取当前硬盘的前 512 字节的扇区(扇区0),该扇区被称为【主引导记录,Master Boot Record】,其通常由两个必要的部分组成:位于 MBR 最头部的、由操作系统指定的微型启动程序(OS-specific bootstrapping program)和位于该程序之后的本磁盘分区表(partation table)。然而,BIOS 并不需要关心这么多,BIOS 接下来做的就只是将 MBR 内的数据加载到内存的 0x7c00 处并跳转到那里开始执行 MBR 中的程序。
在这里提到的 【 MBR 中被指定的程序 】,在 windows 中可能是 Windows MBR loader,在 Linux 中可能是 LILO 或 GRUB ,当然也可能是个病毒程序。
而分区表就不像启动程序那么多样了,分区表是有标准的:分区表是一个 64 字节大的区域,其共有四个 16 字节长的条目,这些条目描述了这块硬盘是被如何划分的(正因如此,你可以在同一块磁盘中运行多个操作系统,或将同一块磁盘划分为多个相互独立的分区)。传统的 Microsoft MBR 代码会读去分区表,找到其中唯一一个被标记为“活动 active” 的分区,加载那个分区的启动扇区并运行其中的代码。【启动扇区】是指分区的第一个扇区,而不是整块磁盘的第一个扇区。如果分区表有问题导致无法正常读取,屏幕上可能会显示类似于 “Invalid Partition Table” 或 “Missing Operating System” 的错误信息。该信息并非由 BIOS 给出,而是由已经从磁盘中加载的 MBR 代码给出的,因此该信息的具体内容由 MBR 中的程序决定。
启动加载( Boot loading )过程在这些年来逐渐丰富和灵活,Linux 启动加载器 Lilo 和 GRUB 可以用于处理非常多类型的操作系统、文件系统和启动配置。它们的 MBR 代码逻辑未必和上面描述的“在活动分区启动”一致,功能实现的过程也可能是下面这样的:
- MBR 本身只包含启动加载器第一阶段的代码,GRUB 称之为“阶段1”( Stage 1 )
- MBR 中留给代码的空间非常非常小,只够用来从磁盘的其他扇区中加载额外所需的启动代码。该扇区也许是一个分区的启动扇区(该分区的第一个扇区),也有可能是在 MBR 安装时被硬编码进 MBR 程序的其他扇区
- MBR 代码和上面第二步中加载进来的代码合并,然后读取一个包含了启动加载器第2阶段的文件。在 GRUB 中被称为“阶段2”( Stage 2 ),在 Windows 中是
C:\NTLDR
,在 Windows 中,若上面的第二步失败,则电脑会显示一个类似于 “NTLDR is missing ” 的信息。阶段2的代码会读取一个启动配置文件(在 GRUB 中是 grub.conf ,在 Windows 中是 boot.ini)。若有多个启动选项可选,接下来会提示用户选择启动项。若只有一个启动项,则会直接启动。 - 这时,就到了需要启动加载器启动内核了。为此,启动加载器必须能够读取启动分区的文件系统。在 Linux 中,加载器需要读取一个包含内核的文件(类似于 “vmlinuz-2.6.22-14-server” ),将该文件加载进内存并跳转到内核启动代码处。在 Windows Server 2003 中,一部分内核启动代码内嵌在 NTLDR 中,和内核镜像本身是分开的 —— 在进行一系列的初始化后,NTLDR 会从
C:\Windows\System32\ntoskrnl.exe
加载内核,接下来就会像 GRUB 一样,跳转到内核的程序入口点。
还有一些复杂的东西值得一提(启动的过程有些 hacky):当前的 Linux 内核文件,即便是经过压缩之后,大小也超过了实模式下可用的 640K RAM ,比如 Ubuntu vanilla 内核压缩后也有 1.7 MB 那么大。然而加载器必须在实模式下运行才能调用 BIOS 路由来从磁盘读取数据,这就不好办了(因为内核还没有起来,所以没办法通过内核进行 I/O,所有的 I/O 此时只能走 BIOS)。解决方案是使用 unreal mode :unreal mode (伪实模式)并不是一个真正的 CPU 模式,而更像是一个使程序能够在实模式和保护模式之间来回切换、以实现通过 BIOS 对 1MB 以上的内存进行访问的方式。若你阅读 GRUB 的源代码,就能够看到代码中到处都在进行这样的切换(在 stage2/ 下查看对 real_to_prot
和 prot_to_real
的调用)。在这个过程的最后,加载器将内核完全装入内存,但此时 CPU 仍在实模式下工作。
至此,我们到了第一章图里面的“Early Kernel Initialization”部分。接下来就该展开内核并进行后续工作了。下一篇博客会带大家走一遍 Linux 内核初始化的过程,期间也会通过 Linux Cross Reference 引用一些代码。我没办法对 windows 做相同的讲解,但还是会指出相应的重点部分
今天的文章【译】电脑是如何启动的分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/20805.html