从系统加电到 Shell
开发操作系统之前,首先需要了解底层硬件的基本工作方式和原理。在 汇编语言:从机器码到现代应用的底层探索 中介绍了传统计算机体系结构,也在 CPU 工作模式 中提到了 CPU 的三种工作模式。这里以系统加电到进入用户程序 Shell 的过程为例,理清从硬件到用户软件经历了哪些层次,以及需要完成哪些工作才能进入用户空间。
第 1 层:固件程序层
CPU 上电复位后,会将寄存器初始化为预设值:
- CS
指向烧录在主板 ROM 中的 BIOS(基本输入输出系统)程序入口地址,即 0xFFFF0;EIP = 0x0000FFF0,CS = 0xF000 - 处理器处于 16 位实模式,关闭分页,即 CR0 = 0x60000010
- 中断处于停用状态,即 EFLAGS = 0x00000002
目前 BIOS 程序分为 Legacy 和 UEFI 两类,下面分别说明。
Legacy BIOS
Legacy BIOS 是传统的固件程序,只能运行在 16 位实模式下,主要限制包括:
- 只能使用 1MB 内存
- MBR 限制:512 字节主引导记录(446 字节引导代码 + 64 字节分区表 + 2 字节签名 0x55AA)
- 仅支持 ≤4 个主分区(或 3 主分区 + 1 扩展分区)
- 最大支持 2TB 磁盘
- 无文件系统驱动(需直接读取磁盘扇区)
其界面和功能相对简单,执行流程如下:
- 进行 POST(Power-On Self Test)自检,检查基本的输入输出设备。若检查失败,主板一般会发出滴滴声或闪烁信号灯
- 在物理地址 0x0000 建立中断向量表(IVT),填充 BIOS 中断服务程序(如 INT 0x10 屏幕输出、INT 0x13 磁盘访问)
- 加载 BIOS 设置,BIOS 读取存储在 CMOS 中的配置参数(如启动顺序、硬盘设置等),执行固件初始化(Firmware Initialization),初始化 CPU、内存控制器、芯片组、总线(PCIe、USB、SATA 等)、时钟等
- 查找引导设备,BIOS 按照设置的启动顺序查找引导设备(如硬盘、光驱、USB 等),即按顺序读取设备最前面的 512 字节。如果这 512 字节的最后两个字节是 0x55 和 0xAA,表明该设备可用于启动;否则表明设备不可用于启动,控制权转交给启动顺序中的下一个设备
- 加载 Bootloader,BIOS 将第一个可引导设备的主引导记录(MBR)加载到内存 0x7C00 处,并将控制权交给 Bootloader
注:传统意义上的 bootloader(如 GRUB)的一部分实现就位于 MBR 中。
UEFI
UEFI(统一可扩展固件接口)没有 16 位实模式的限制,会更早进入保护模式以使用更大的内存,实现更复杂的硬件管理功能。其工作流程如下:
- SEC(Security):安全验证阶段,进行硬件初始化和验证,确保系统安全。该过程相当于 BIOS 中的 POST 自检
- PEI(Pre-EFI Initialization):EFI 前期初始化,进行内存和其他关键资源的初始化
- DXE(Driver Execution Environment):驱动执行环境,初始化大部分硬件驱动和功能
- BDS(Boot Device Selection):启动设备选择,确定从哪个设备加载操作系统
- TSL(Transition to SMM):过渡到系统管理模式,准备操作系统加载
- RT(Runtime):系统运行阶段,操作系统已加载并运行
- AL(Advanced Launch):高级启动,可能涉及灾难恢复或关机过程
由于 UEFI 在操作系统启动前就已初始化和加载大部分硬件驱动,因此在加载操作系统前就能完整利用大部分硬件功能,例如直接识别文件系统、通过显卡显示丰富的图形界面以及使用网络功能等。它会直接在 ESP(EFI System Partition)中查找特定路径的引导程序(如 /EFI/BOOT/BOOTX64.EFI)进行启动。
注:在 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 前半段,包含 disk driver modules)
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,跳转执行
注 1:GRUB2 在执行过程中会进入保护模式,但后续内核会覆盖修改 GDT 等相关配置。
注 2: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 发行版)
1. 读取配置文件
/etc/systemd/system.conf/usr/lib/systemd/system.conf(发行版默认)- 管理单元文件:
/etc/systemd/system/(本地自定义)、/usr/lib/systemd/system/(系统默认)
2. 确定启动目标(Target)
graphical.target:图形界面多用户模式multi-user.target:命令行多用户模式rescue.target:单用户维护模式- 默认目标通过
/etc/systemd/system/default.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 等),进入图形桌面环境
核心机制
- 依赖管理:服务之间通过 .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
GNU GRUB 手册
JamesM’s kernel development tutorials
Why BIOS loads MBR into 0x7C00 in x86 ?
How to create an OS from scratch - github
The little book about OS development
The Linux Boot Process: From Power Button to Kernel
从零开始写 OS 内核
南京大学:操作系统:设计与实现(2023 春季学期)
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
部分信息可能已经过时






