linux kernal 启动路线一览

这篇文章介绍了 linux 系统下从启动电源到用户看到 shell 的总体流程

四个阶段总览

1
2
3
4
5
6
7
8
9
10
11
阶段 1:固件阶段
CPU → BIOS / UEFI → bootloader

阶段 2:Bootloader 阶段
bootloader → kernel / initramfs → kernel entry

阶段 3:Kernel 阶段
kernel 初始化 → 挂载根文件系统 → 启动 PID 1

阶段 4:用户态初始化阶段
init / systemd → 用户态服务 → 登录入口 → shell / GUI

阶段 1:固件阶段

目标:让机器从上电状态进入 bootloader。

1
2
3
4
5
6
7
8
9
CPU 通电 / 复位

CPU 从固定入口开始执行 BIOS / UEFI 固件

BIOS / UEFI 做硬件自检和早期硬件初始化

BIOS / UEFI 根据启动顺序找到 bootloader

BIOS / UEFI 将 bootloader 加载到内存并交给它执行

这一阶段的核心:

1
固件负责找到 bootloader。

这一阶段主要有两条路线:

1
2
传统路线:BIOS + MBR
现代路线:UEFI + GPT

1. BIOS + MBR

BIOS 是什么?

BIOS 全称:

1
Basic Input/Output System

它是主板上的固件程序。CPU 上电后会先执行 BIOS。

BIOS 主要负责:

1
2
3
4
硬件自检
早期硬件初始化
选择启动设备
读取启动设备的第一个扇区

MBR 是什么?

MBR 全称:

1
Master Boot Record

它位于启动磁盘的第一个逻辑扇区,也就是:

1
LBA 0

MBR 通常是 512 字节:

1
2
3
4
5
MBR / 512 bytes

┌──────────────┬──────────────┬──────────┐
│ boot code │ partition │ 0x55AA │
└──────────────┴──────────────┴──────────┘

MBR 里面有一小段启动代码,但空间很小,所以它通常只负责加载后续 bootloader。


BIOS 和 MBR 怎么加载 bootloader?
1
2
3
4
5
6
7
8
9
10
11
BIOS

选择启动磁盘

读取磁盘 LBA 0,也就是 MBR

检查 MBR 末尾是否是 0x55AA

把 MBR 加载到内存并执行

MBR boot code 继续加载后续 bootloader

核心理解:

1
2
BIOS 不认识 Linux / Windows;
BIOS 只负责读取启动磁盘第一个扇区并执行。

2. UEFI + GPT

UEFI 是什么?

UEFI 全称:

1
Unified Extensible Firmware Interface

它是 BIOS 的现代替代方案。

UEFI 不再只会读取磁盘第一个扇区,而是能根据启动项去读取指定的 .efi 启动文件。

UEFI 主要负责:

1
2
3
4
5
硬件自检
早期硬件初始化
读取启动项
找到 EFI 系统分区
加载 .efi bootloader

GPT 是什么?

GPT 全称:

1
GUID Partition Table

它是现代磁盘分区表,用来替代 MBR 分区表。

GPT 的作用是描述磁盘分区:

1
2
3
有哪些分区
每个分区在哪里
哪个分区是 EFI 系统分区

GPT 自己不负责执行代码,它只是磁盘的“分区地图”。


ESP 是什么?

ESP 全称:

1
EFI System Partition

它是 EFI 系统分区,通常是 FAT32 文件系统。

里面放 .efi 启动文件,例如:

1
2
3
/EFI/ubuntu/grubx64.efi
/EFI/Microsoft/Boot/bootmgfw.efi
/EFI/BOOT/BOOTX64.EFI

UEFI 和 GPT 怎么加载 bootloader?
1
2
3
4
5
6
7
8
9
10
11
UEFI

读取 NVRAM 中的启动项

根据 GPT 找到 ESP

进入 ESP 文件系统

按路径读取 .efi 文件

加载并执行 .efi bootloader

核心理解:

1
2
UEFI 不再执行磁盘第一个扇区的启动代码;
UEFI 根据启动项加载 EFI 分区里的 .efi 启动文件。

3. BIOS + MBR 和 UEFI + GPT 对比

对比项 BIOS + MBR UEFI + GPT
启动入口 磁盘第一个扇区 EFI 分区里的 .efi 文件
分区表 MBR GPT
bootloader 位置 MBR 代码 + 后续阶段 ESP 中的 .efi 文件
固件能力 比较原始 更现代
文件系统能力 基本不依赖文件路径 能读取 ESP 文件系统
多系统启动 容易争抢 MBR 各系统可放自己的 .efi 文件
大磁盘支持 较弱 较好
安全能力 较弱 可支持 Secure Boot
典型时代 传统 PC 现代 PC

阶段 2:Bootloader 阶段

目标:让 bootloader 找到并启动内核。

1
2
3
4
5
6
7
8
9
10
11
bootloader 开始执行

bootloader 读取自己的配置

bootloader 找到 kernel / initramfs

bootloader 将 kernel / initramfs 加载到内存

bootloader 准备启动参数

bootloader 跳转到 kernel 入口

这一阶段的核心:

1
bootloader 负责找到 kernel,并把控制权交给 kernel。

这一阶段可以结合 GRUB 来理解,它是一种常见的 bootloader,Linux 系统中非常常见。


1. GRUB 是什么?

GRUB (GRand Unified Bootloader) 是固件和内核之间的启动程序。

它的位置在:

1
2
3
4
5
BIOS / UEFI

GRUB

Linux kernel

可以把 GRUB 理解成:

1
GRUB = 一个能读配置、能读文件系统、能加载内核的智能 bootloader

2. GRUB 从哪里来?

GRUB 不是硬件自带的,而是在安装系统或安装 bootloader 时写入磁盘的。

在 BIOS + MBR 模式下:

1
2
3
4
5
BIOS

MBR 中的 GRUB 启动代码

GRUB 后续阶段

在 UEFI + GPT 模式下:

1
2
3
4
5
UEFI

ESP 中的 grubx64.efi

GRUB

3. GRUB 读取什么配置?

GRUB 会读取自己的配置文件,常见是:

1
/boot/grub/grub.cfg

配置中会描述:

1
2
3
4
5
有哪些启动项
默认启动哪个
kernel 文件在哪里
initramfs 文件在哪里
要传给 kernel 什么参数

一个简化示例:

1
2
3
4
menuentry "Linux" {
linux /boot/vmlinuz-linux root=UUID=xxxx rw quiet
initrd /boot/initramfs-linux.img
}

其中:

1
linux /boot/vmlinuz-linux ...

表示加载 Linux 内核文件。

1
initrd /boot/initramfs-linux.img

表示加载 initramfs。

1
root=UUID=xxxx rw quiet

表示传给 kernel 的启动参数。


4. kernel 和 initramfs 分别是什么?

kernel 是操作系统内核本体,GRUB 负责把它加载到内存,并跳到它的入口。

initramfs 是一个早期临时根文件系统,它通常由 bootloader 和 kernel 一起加载。

可以简单理解为:

1
2
kernel 是内核本体;
initramfs 是帮助 kernel 早期启动的临时工具箱。

5. GRUB 怎么启动 kernel?

GRUB 的核心流程如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
GRUB 开始执行

读取 grub.cfg

显示启动菜单

选择某个 menuentry

加载 kernel 到内存

加载 initramfs 到内存

准备 kernel command line

执行 boot

跳转到 kernel 入口

示意图:

1
2
3
4
5
6
GRUB
├── 读取配置 grub.cfg
├── 找到 /boot/vmlinuz-linux
├── 找到 /boot/initramfs-linux.img
├── 准备 root=... quiet rw 等参数
└── 跳转到 kernel entry

从这一刻开始:

1
GRUB 退出主线,kernel 接管机器。

阶段 3:Kernel 阶段

目标:让内核接管机器,并启动第一个用户态进程。

1
2
3
4
5
6
7
kernel 接管 CPU

kernel 进行内核态初始化

kernel 挂载根文件系统 /

kernel 启动第一个用户态进程 init / systemd

这一阶段的核心:

1
kernel 负责建立内核世界,并启动 PID 1。

1. kernel 为什么要初始化?

bootloader 已经把 kernel 加载到内存,并跳转到 kernel 入口,但是此时很多核心能力还没有建立:

1
2
3
4
5
6
7
没有完整的内存管理
没有完整的中断处理
没有调度器
没有驱动系统
没有文件系统
没有用户态进程
没有系统调用服务

所以 kernel 进入后,第一件事是:

1
先把自己运行所需的基础设施搭起来。

2. kernel 初始化的主要工作

kernel 初始化的目标是:

1
让内核具备管理硬件、运行程序、提供系统服务的能力。

主要包括:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
kernel 初始化
├── CPU 初始化
│ ├── 设置内核栈
│ ├── 设置异常入口
│ ├── 设置中断入口
│ └── 设置系统调用入口

├── 内存管理
│ ├── 管理物理内存
│ ├── 建立虚拟内存
│ ├── 建立页表
│ └── 提供内核内存分配能力

├── 中断和异常
│ ├── 处理 CPU 异常
│ ├── 处理硬件中断
│ └── 支持时钟中断

├── 调度器
│ ├── 建立进程 / 线程模型
│ ├── 建立运行队列
│ └── 支持任务切换

├── 驱动
│ ├── 识别硬件设备
│ ├── 初始化磁盘、键盘、网卡等设备
│ └── 为内核提供访问硬件的能力

├── VFS / 文件系统
│ ├── 建立统一文件访问接口
│ ├── 支持挂载文件系统
│ └── 支持查找和读取文件

└── 系统调用
├── 建立用户态进入内核态的入口
└── 向用户程序提供内核服务

这些初始化工作共同服务于一个目标:

1
让用户态程序能够安全地运行,并通过系统调用使用系统资源。

3. 为什么要挂载根文件系统?

kernel 要启动第一个用户态进程,但是用户态进程本质上是一个文件,例如:

1
2
3
/sbin/init
/lib/systemd/systemd
/bin/sh

这些文件都存放在文件系统里。

因此 kernel 必须先挂载根文件系统:

1
2
3
4
5
某个磁盘分区

挂载为 /

得到完整目录树

4. kernel 怎么接力给 init / systemd?

根文件系统挂载完成后,kernel 会尝试启动第一个用户态进程。

这个进程通常叫 init ,现代 Linux 里经常实际是 systemd

在 wsl 上 init 是指向 systemd 的符号链接:

1
lrwxrwxrwx 1 root root 22 Mar 14 00:48 /sbin/init -> ../lib/systemd/systemd*

它的特殊之处是:

1
它是第一个用户态进程,PID = 1。

流程大致是:

1
2
3
4
5
6
7
8
9
10
11
kernel 挂载根文件系统 /

kernel 找到 init 程序

kernel 创建第一个用户态进程

kernel 加载 init 的可执行文件

kernel 切换到用户态

init / systemd 开始执行

从这一刻开始:

1
2
kernel 不再继续主动启动所有用户服务;
用户态初始化由 PID 1 负责。

阶段 4:用户态初始化阶段

目标:让用户态系统跑起来,并提供登录入口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
init / systemd 作为 PID 1 开始执行

init / systemd 启动用户态服务
- udev
- 日志
- 网络
- sshd
- getty
- display manager

系统提供登录入口
- 本地文本登录:getty → login → shell
- 远程登录:sshd → shell
- 图形登录:display manager → desktop session

用户通过 shell / GUI 使用系统

这一阶段的核心:

1
PID 1 负责展开用户态世界,并让用户能够登录和使用系统。

总结:

1
2
3
4
固件找到 bootloader;
bootloader 找到 kernel;
kernel 建立内核世界并启动 PID 1;
PID 1 展开用户态世界并提供登录入口。