开机流程
开机流程涉及硬件态向内核态的转化,操作系统是运行在硬件上的一个程序,其加载运行也是一个复杂的过程,一旦操作系统运行成功,应用层的软件便可以由操作系统加载和运行,乘上操作系统的快车,利用操作系统提供的 API 便捷地使用硬件完成业务需求。
供电
在硬件平台通电时,主板等周边设备上的小型专用处理器将优先工作。其中挂载在 CPU 总线上的小型控制器 EC(Embedded Controller)负责计算机的电源管理工作。EC 控制电源供应单元为硬件系统供电,系统各部分进入各自的供电流程,包括 CPU、内存、南北桥芯片等。
周边设备的控制器芯片不承担主要的计算任务,但会帮助协调各个硬件之间的工作。
各个硬件的供电顺序有所先后,一般顺序是:CPU、内存、南北桥、扩展卡和外围设备、启动存储等。电源控制单元在确保电源供应一切顺利后,向 EC 发出信号表示供电完毕。供电成功后,EC 通知主板上的各个芯片组,正式开启计算机的启动流程。
首先,南北桥进行交互:
- 南桥向北桥发出正常信号
- 北桥收到南桥信号,并向 CPU 发送正常信号
- CPU 开始工作
供电单元可以进行交流电到直流电的转化,并保持电源电压的稳定。
BIOS/UEFI 寻找 Boot Loader
CPU 开始工作后的第一个程序是 BIOS/UEFI 程序,它是主板上的固件。BIOS 程序会:
- 扫描和检查设备
- 初始化硬件系统(主板、内存、CPU、显卡等)
- 确保设备能够正常工作(POST - Power-On Self Test)
- 为内存条和 MMIO 设备分配地址空间
- 将地址空间连成连续的数组空间(地址位数取决于机器位宽,通常为 32 位或 64 位)
在硬件检查完毕后,BIOS/UEFI 程序尝试依次从多个外部存储设备中加载 Boot Loader 程序到内存中。如果某个设备中没有找到,则尝试从下一个存储设备加载。一旦加载成功,计算机的执行权就移交给 Boot Loader。
BIOS 加载 Boot Loader 时,从外部存储设备的启动扇区(MBR - Master Boot Record)加载,并将其放置到内存中的约定区域。BIOS 查找 Boot Loader 程序的顺序可能如下:
- 硬盘
- USB
- CDROM
- 网卡(pxe 启动)
BIOS 通常提供基于终端的配置界面,允许用户自定义引导过程,例如修改启动介质的优先级。
一些硬件层面的功能(如网卡的 SR-IOV 功能)不一定会被启用。如需启用,需要重启机器并提前修改 BIOS 配置。
Boot Loader 加载操作系统
Boot Loader 是存放在操作系统镜像盘中约定区域的一段程序。它被 BIOS 程序加载并执行,负责:
- 把操作系统的文件加载到内存中
- 初始化操作系统
- 将计算机的执行权移交给操作系统
Boot Loader 执行时,CPU 处于 Real 模式,只能访问 1MB 的内存空间,没有内存保护。Boot Loader 利用硬盘的分区表、文件系统信息和操作系统核心文件,实现从实模式到保护模式的切换,以及从硬盘到内存的数据传输。
Boot Loader 根据 MBR 中的磁盘分区信息,找到活动分区(操作系统文件所在的分区),然后:
- 找到操作系统可执行文件
- 加载操作系统到指定的内存区域
- 跳转 CPU 到该区域,开始执行操作系统
常见的 Boot Loader 实现包括:
- GRUB(广泛用于 Linux 系统)
- LILO
- NTLDR
- BOOTMGR
在 Linux 文件系统中,Boot Loader 通常位于/boot
目录下。
操作系统启动
当 Boot Loader 将操作系统内核加载到内存并将控制权移交后,操作系统的启动过程正式开始。
linux 第一行代码是与体系结构相关的汇编代码,位于 arch
文件夹中,主要执行硬件初始化工作和 C 语言运行时初始化工作,以屏蔽不同的硬件结构来启动 C 语言的能力,接着解压和加载内核镜像,跳转执行内核代码。
- 验证 Bootloader 环境:检查 boot_params,确保协议兼容。
- 设置初始栈和清零 BSS:提供栈和初始化全局变量。
- 切换到保护模式:初始化 GDT,禁用中断。
- 调用早期 C 代码:解析硬件信息,准备跳转。
- 解压内核镜像:解压 vmlinuz,设置 vmlinux 入口。
- 切换到长模式:启用 64 位,设置页表。
- 初始化 64 位环境:设置 GDT、IDT、栈。
- SMP 初始化:唤醒多核。
- 调用 x86_64_start_kernel(在 x86 上):进入 C,调用 start_kernel 函数
init/main.c/start_kernel
函数是内核主体逻辑的入口函数,在该函数中会启动核心的管理子系统,包括进程管理、内存管理、文件系统、设备驱动等。这些子系统为上层应用提供了统一的接口和服务,屏蔽了底层硬件的复杂性。各个子系统的初始化大致遵循如下先后顺序(简化版):
- 早期内核初始化(init_task、锁调试)。
- 内存管理初始化(物理内存检测、页表建立、内存分配器初始化)
- 中断系统初始化(设置中断向量表、注册 ISR、初始化中断控制器)
- 定时器/时钟子系统初始化(初始化系统定时器,为后续调度和时间管理做准备)
- 调度器和进程管理初始化(初始化调度器、创建内核线程、进程表等)
- 设备驱动和文件系统初始化(初始化块设备、字符设备、挂载根文件系统等)
- 显式使能中断(如执行 sti 指令,允许 CPU 响应中断)
- 启动用户空间第一个进程(如 init 或 systemd)
一号进程启动
接下来,内核会启动第一个用户空间进程(在 Linux 系统中通常是 init 或 systemd),该进程负责进一步启动系统服务和用户环境。
一号进程(PID 1)在 Linux 操作系统中具有极其特殊的地位。它是用户空间启动的第一个进程,由内核直接创建,并且始终拥有进程号 1。由于其特殊的启动顺序和地位,一号进程承担着整个系统用户空间初始化的重任,负责启动系统服务、守护进程以及用户登录环境,决定了系统的运行模式和服务框架。