从系统加电到 Shell
开发操作系统之前,需要了解底层硬件的基本工作方式。在 [汇编语言:从机器码到现代应用的底层探索](../language/x86 汇编语言入门.md) 中介绍了传统计算机体系结构,在 CPU 工作模式 中提到了 CPU 的三种工作模式。本文以系统加电到进入 Shell 的过程为主线,逐层拆解硬件、固件、引导加载器、内核、用户空间各自完成了什么工作,以及这些设计背后的硬件约束和工程取舍。
第 1 层:固件程序层
CPU 上电复位后,寄存器被硬连线为预设值:CS
目前 BIOS 程序分为 Legacy 和 UEFI 两类,下面分别说明。
Legacy BIOS
Legacy BIOS 是传统的固件程序,只能运行在 16 位实模式下。这个限制来自 1981 年 IBM PC 的设计,当时 8086 CPU 只有 20 根地址线,最大寻址 1MB,BIOS 的整个接口体系(INT 0x10 屏幕输出、INT 0x13 磁盘访问等中断服务)都建立在 16 位实模式之上,此后 40 年的硬件迭代都无法打破这个兼容性枷锁。
实模式的约束直接决定了 Legacy BIOS 的能力边界:只能使用 1MB 内存;引导记录被限制在 MBR 的 512 字节(446 字节引导代码 + 64 字节分区表 + 2 字节签名 0x55AA),因为 MBR 必须装进一个扇区;分区表只有 4 个条目,最多支持 4 个主分区(或 3 主分区 + 1 扩展分区);使用 32 位 LBA 寻址,磁盘上限 2TB;没有文件系统驱动,只能直接读取磁盘扇区,这意味着引导加载器必须自己实现文件系统解析。
Legacy BIOS 的执行流程如下:首先进行 POST(Power-On Self Test)自检,检查基本输入输出设备,失败时主板发出蜂鸣或闪烁信号灯;然后在物理地址 0x0000 建立中断向量表(IVT),填充 BIOS 中断服务程序;接着读取 CMOS 中的配置参数(启动顺序、硬盘设置等),执行固件初始化,初始化 CPU、内存控制器、芯片组、总线(PCIe、USB、SATA)、时钟等;之后按启动顺序逐个查找引导设备,读取每个设备最前面的 512 字节,如果末尾两个字节是 0x55 和 0xAA 就认为该设备可引导;最后将可引导设备的 MBR 加载到内存 0x7C00 处,将控制权交给 Bootloader。
0x7C00 这个地址并非随意选择。IBM PC 5150 的设计者决定将 DOS 载入到 0x0000 开始的低地址,DOS 最少需要约 32KB 内存,0x7C00 = 32KB - 1024B,刚好给 MBR 留出空间又不与 DOS 冲突。这个地址从此成为事实标准,即使现代系统早已不需要这个约束,仍然沿用至今。
传统意义上的 bootloader(如 GRUB)的一部分实现就位于 MBR 中。
UEFI
UEFI(统一可扩展固件接口)抛弃了 16 位实模式的兼容性包袱,在极早期就切换到保护模式以使用更大的地址空间。这个设计选择牺牲了与旧 BIOS 中断服务的兼容性(UEFI 程序不能调用 INT 0x10 等),换来的是:支持 GPT 分区表(突破 2TB 限制和 4 分区限制)、内置文件系统驱动(可直接读取 FAT32 上的 EFI 文件)、原生图形输出协议(GOP,不依赖 VGA)、网络协议栈等。
UEFI 将启动过程划分为 7 个阶段,每个阶段有明确的职责边界和状态转换条件:
SEC(Security)阶段进行硬件初始化和验证,相当于 BIOS 的 POST 自检,但增加了固件完整性校验(Secure Boot)。PEI(Pre-EFI Initialization)阶段初始化内存和其他关键资源,此时内存还不可用,PEI 代码只能使用 CPU 寄存器和 ROM 中的临时存储(称为 CAR,Cache-as-RAM)。DXE(Driver Execution Environment)阶段是工作量最大的阶段,初始化大部分硬件驱动和功能,此时内存已可用,可以加载和执行复杂的驱动模块。BDS(Boot Device Selection)阶段确定从哪个设备加载操作系统。TSL(Transition to SMM)阶段准备操作系统加载。RT(Runtime)阶段操作系统已接管。AL(Advanced Launch)阶段涉及灾难恢复或关机。
这种分阶段设计的核心动机是信任链的逐级建立:SEC 验证 PEI,PEI 验证 DXE,DXE 验证 BDS,每一级只信任已验证的下一级代码,未经验证的代码不允许执行。这是 Secure Boot 的基础。
由于 UEFI 在操作系统启动前就已初始化大部分硬件驱动,在加载操作系统前就能完整利用硬件功能,直接识别文件系统、通过显卡显示图形界面、使用网络等。它直接在 ESP(EFI System Partition)中查找特定路径的引导程序(如 /EFI/BOOT/BOOTX64.EFI)进行启动,不再需要 MBR 那种逐扇区扫描的方式。
在 UEFI+GPT 方案中,GRUB 以 EFI 文件形式存在于 ESP 分区,不再依赖 MBR。
第 2 层:引导加载层
BIOS/UEFI 的最后一项工作是找到引导加载器并将其加载到内存的 0x7C00 处,然后开始调用。常见的引导加载器(Boot Loader)有 GRUB2 和 LILO 等。
这里主要介绍 GRUB2,它可以引导多个操作系统,可直接读取文件系统,拥有图形和文本界面,通过配置文件设置引导项的内核及启动参数等,也可在启动页面通过指令进行动态修改。其主要工作是将选定引导项的内核启动参数传递给内核,将内核和 initramfs(如果存在)加载到内存中,然后转移控制权。
GRUB2 在 BIOS 模式下的启动分为四个阶段:
阶段 1(boot.img):BIOS 开机后执行 MBR(第一个扇区,512 字节),MBR 的前 446 字节由 GRUB2 的 boot.img 占据。由于空间很小,只能初始化最基本的执行环境并加载下一阶段(core.img)。
阶段 1.5(core.img 前半段,包含磁盘驱动模块):MBR 的后续「空闲扇区」中,GRUB 会放置 core.img(远大于 512 字节)。core.img 包含文件系统驱动(ext4、xfs、btrfs 等)、磁盘访问模块、压缩和解压工具。这使得 GRUB 拥有了读取磁盘文件系统的能力,并能从磁盘加载必要的模块和配置以进入下一阶段。
阶段 2(加载 /boot/grub 下的模块和配置文件):core.img 找到 /boot/grub/grub.cfg,加载必要的模块(如 ext2.mod、part_msdos.mod、normal.mod 等),初始化完整的 GRUB2 环境。此时 GRUB 进入 normal mode,可进行图形界面或命令行操作,支持解析配置(内核路径、initrd、内核参数等)以及高级功能(LVM、RAID、加密分区支持)。
阶段 3(加载 Linux 内核与 initrd/initramfs):根据 grub.cfg 的配置,执行类似下面的命令:
linux /boot/vmlinuz-xxx root=/dev/sda1 ro quietinitrd /boot/initrd-xxx.imgboot将 Linux 内核(vmlinuz)和 initrd 装入内存,跳转执行内核,内核接管控制权。
UEFI
UEFI 下没有 MBR 的 512 字节限制,流程更简单:
- UEFI 固件从 EFI 系统分区(ESP)加载 grubx64.efi
- grubx64.efi 内部包含 GRUB2 的核心功能(类似 BIOS 模式的 core.img)
- 加载 /boot/grub/grub.cfg 和其他模块
- 显示菜单,加载内核 + initrd,跳转执行
GRUB2 在执行过程中会进入保护模式,但后续内核会覆盖修改 GDT 等相关配置。 initramfs 是进入真正的根文件系统之前的一个垫片,通常包含必要的设备驱动程序、工具、配置文件以及运行这些工具的环境。引导加载器直接将它和内核加载到内存。这样内核可以更加精简(不必将不同格式的磁盘驱动直接编译进内核),并且在挂载真正根文件系统前也可完成加密解密等初始化操作。
这一层也是可以着手编码的层。
第 3 层:内核初始化层
当引导加载程序(如 GRUB)成功将操作系统的内核映像加载到内存中,并将 CPU 的控制权移交给内核入口点(通常是 _start 或类似符号)时,就进入了内核初始化层。此时内核面对的是一个几乎原始的硬件环境,没有内存管理、中断处理、文件系统,也没有进程等概念。内核初始化层的任务是建立这些子系统,将硬件抽象化,直到准备好执行第一个用户空间进程(通常为 init 或 systemd)。
1. 架构相关初始化(Architecture-Specific Setup)
- CPU 模式切换:在 x86 上,从实模式进入保护模式/长模式(64 位),设置段寄存器
- 内存管理:建立初始页表,启用分页(MMU),保留引导信息中的物理内存布局
- 中断机制:设置 GDT(全局描述符表)、IDT(中断描述符表),初始化 APIC/IOAPIC
- CPU 特性检测:检测并启用 CPU 扩展功能(FPU、MMX、SSE 等)
- 早期输出:初始化 early console 以便 printk() 能输出调试信息
- 内核解压:bzImage 格式的内核先在低地址执行解压,再把完整内核搬到高地址运行
2. 内核通用初始化(start_kernel())
进入内核 C 语言入口点 start_kernel(),这里会初始化几乎所有核心子系统:
- 内存管理:伙伴系统初始化(物理页分配器),虚拟内存子系统建立
- 进程调度器:sched_init() 初始化调度框架
- 同步机制:初始化 RCU(Read-Copy Update)等高性能同步机制
- 时钟和定时器:time_init() 设置系统时钟和定时器中断
- 内核日志:printk() 正式启用环形缓冲
- 安全机制:初始化 Linux Security Modules(LSM)
- 虚拟文件系统:初始化 VFS、挂载内存中的临时根文件系统 rootfs(ramfs/tmpfs)
- 设备模型:建立总线/设备/驱动模型,准备后续设备探测
3. 关键内核线程和进程
在 rest_init() 中,内核启动几个关键进程:
- 0 号进程(idle 或 swapper):最初的内核上下文,最终进入空闲循环
- 1 号进程(kernel_init):内核初始化线程,后续会尝试执行 /init 或 /sbin/init
- 2 号进程(kthreadd):内核线程管理器,负责创建其他内核线程
4. initramfs 处理
如果引导加载器提供了 initramfs,内核会:
- 解压 initramfs 到内存中的 rootfs
- 将其作为临时根文件系统使用
- 尝试执行 initramfs 内的 /init 程序(第一个用户态程序)
如果没有 initramfs,或 /init 执行失败,内核会直接查找并执行指定根文件系统上的 /sbin/init、/bin/init 或 /bin/sh。
initramfs 本质上是一个临时的基于内存的根文件系统,使命是为挂载真实的根文件系统做准备。内核启动 initramfs 后,会运行其中的 /init 脚本或可执行文件,最后切换到真正的用户根文件系统。
第 4 层:用户空间层
当真正的根文件系统挂载完成后,内核会启动第一个用户空间进程 /sbin/init(现代系统多为 systemd,也可能是 SysV init、OpenRC 等)。它成为 PID 1,是所有用户空间进程的祖先进程。
systemd 启动流程(主流 Linux 发行版)
systemd 的启动流程按顺序分为四步。
第 1 步:读取配置文件。systemd 读取 /etc/systemd/system.conf(本地覆盖)和 /usr/lib/systemd/system.conf(发行版默认),以及管理单元文件(/etc/systemd/system/ 本地自定义、/usr/lib/systemd/system/ 系统默认)。
第 2 步:确定启动目标(Target)。systemd 根据默认目标(通过 /etc/systemd/system/default.target 符号链接指定)决定启动到哪个模式:
graphical.target:图形界面多用户模式multi-user.target:命令行多用户模式rescue.target:单用户维护模式
第 3 步:初始化基础系统。挂载必要的虚拟文件系统(/proc、/sys、/dev、/run 等),设置主机名、时区、本地化、随机数种子,初始化内核模块和网络服务(如 systemd-networkd 或 NetworkManager),启动日志服务 systemd-journald、认证服务 systemd-logind、定时任务服务 systemd-timedated 等,并通过 udev 设备管理为 /dev 创建设备节点、响应热插拔事件。
第 4 步:进入用户工作环境。达到 multi-user.target 时系统进入命令行多用户模式(可登录终端);达到 graphical.target 时启动显示管理器(gdm、lightdm、sddm 等),进入图形桌面环境。
systemd 相比传统 init 的核心改进在于依赖管理和并行启动。服务之间通过 .service、.target、.socket 等单元文件声明依赖关系,systemd 会自动分析依赖关系并尽可能并行启动服务,大幅加快启动速度。它还支持多种按需激活机制:Socket Activation(端口有连接时启动服务)、D-Bus Activation(通过 D-Bus 消息按需启动服务)、Path / Mount Activation(监控路径或挂载事件触发服务)。
SysV init 启动流程(对比 systemd)
- 使用 /etc/inittab 定义默认运行级别(0–6)
- 每个运行级别对应 /etc/rcX.d/,包含启动/关闭脚本链接,顺序执行
- 串行启动,缺乏依赖关系管理,启动较慢
第 5 层:登录管理器 / Shell
当 systemd 或 init 启动完目标服务后,系统进入可交互的登录阶段。
文本登录(multi-user.target)
- getty:在虚拟终端(tty1–tty6)启动,显示 login: 提示符
- 认证:用户输入用户名和密码,经由 PAM(Pluggable Authentication Modules) 认证
- 启动 Shell:认证成功后,执行
/etc/passwd中指定的登录 shell(如/bin/bash、/bin/zsh) - 加载配置:shell 执行其初始化配置文件(如
.bashrc、.zshrc)
图形登录(graphical.target)
- 显示管理器(Display Manager, DM):如
gdm、lightdm、sddm- 提供图形化登录界面
- 通过 PAM 完成用户认证
- 桌面会话:认证通过后,DM 启动桌面会话(如 GNOME、KDE Plasma、XFCE、Wayland 或 X11 会话)
- 启动桌面环境核心进程(会话管理器、面板、文件管理器等)
- 执行用户自定义的自动启动脚本(
~/.xinitrc、~/.config/autostart/等)
参考资料
- OSDev Wiki - 操作系统开发百科全书
- GNU GRUB 手册 - GRUB 引导加载器官方文档
- JamesM’s kernel development tutorials - 内核开发入门教程
- Why BIOS loads MBR into 0x7C00 in x86 - BIOS 加载 MBR 地址的历史原因
- How to create an OS from scratch - 从零创建操作系统的步骤化教程
- The little book about OS development - 操作系统开发入门小书
- The Linux Boot Process: From Power Button to Kernel - Linux 启动流程详解
- 从零开始写 OS 内核 - 中文操作系统内核教程
- 南京大学:操作系统设计与实现 - 南京大学操作系统课程
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






