按一下电源到登录提示符出现,中间这几十秒其实跑了四个阶段:固件自检、引导加载器拉起内核、initramfs 把根文件系统准备好、最后 systemd 接管。排启动类故障------比如卡在 GRUB、内核 panic、服务起不来------你得知道每一步在干什么,才知道卡在哪一环。这一篇把整条链路拆开来。
一、全链路一眼看
从上电到登录,顺序固定:
text
BIOS/UEFI 固件自检 → 选择启动设备 → GRUB 引导加载器 → 加载内核 + initramfs
→ 内核挂载 initramfs 临时根 → 加载磁盘/文件系统驱动 → 切到真实根
→ systemd 成为 PID 1 → 激活 default target → 登录提示符
每一环都有对应的排错入口。卡在 BIOS 阶段是硬件/启动盘问题;卡在 GRUB 是引导配置问题;内核 panic 多半是 initramfs 或驱动;进不了登录界面才是 systemd 服务问题。
二、BIOS/UEFI:固件阶段
按下电源,主板上的固件先跑起来。这东西现在分两代:
| 对比项 | Legacy BIOS | UEFI |
|---|---|---|
| 启动方式 | 读磁盘第一扇区 MBR | 读 ESP 分区里的 EFI 程序 |
| 分区表 | MBR | GPT(也支持 MBR) |
| 磁盘大小 | 2TB 上限 | 无此限制 |
| 启动速度 | 慢 | 快,可并行初始化 |
| 引导文件 | MBR 里 512 字节 | FAT32 分区 /EFI/ 目录 |
MBR 那 512 字节里放不下完整引导器,所以 BIOS 时代才搞出 GRUB stage1/stage2 这种分段设计。UEFI 直接认文件,引导器就是 ESP 分区里一个 .efi 文件,清爽很多。
怎么判断自己机器是哪种:
bash
[ -d /sys/firmware/efi ] && echo "UEFI 启动" || echo "Legacy BIOS 启动"
预期输出:
text
UEFI 启动
有 /sys/firmware/efi 就是 UEFI。没有就是老 BIOS。
UEFI 环境下看启动项:
bash
sudo efibootmgr
预期输出(节选):
text
BootCurrent: 0001
Timeout: 1 seconds
BootOrder: 0001,0000,0002
Boot0000* Windows Boot Manager
Boot0001* ubuntu
Boot0002* Hard Drive
BootOrder 决定引导顺序,GRUB 一般就是那个 ubuntu 条目。
三、GRUB:引导加载器
固件把控制权交给 GRUB 之后,GRUB 干三件事:显示启动菜单、选内核、把内核和 initramfs 一起加载到内存。
GRUB 配置文件在哪
| 发行版 | 主配置 | 重新生成命令 |
|---|---|---|
| Ubuntu/Debian | /boot/grub/grub.cfg |
sudo update-grub |
| CentOS/Rocky | /boot/grub2/grub.cfg |
sudo grub2-mkconfig -o /boot/grub2/grub.cfg |
grub.cfg 是生成出来的,别手改。真正改的是 /etc/default/grub(全局默认项)和 /etc/grub.d/ 下的脚本。改完重新生成:
bash
sudo update-grub # Ubuntu
sudo grub2-mkconfig -o /boot/grub2/grub.cfg # Rocky
临时加内核参数
开机卡在内核阶段,临时加个 single 或 systemd.unit=rescue.target 进救援模式,不用改文件:GRUB 菜单出现时按 e,找到 linux 那一行,行末加上参数,按 Ctrl+X 启动。这是排障救命招。
重装 GRUB
系统装在 /dev/sda(UEFI 模式),GRUB 坏了重装:
bash
sudo grub-install /dev/sda
sudo update-grub
Rocky 上是 grub2-install。装完一定要 update-grub 重新生成配置,不然菜单里还是旧条目。
四、initramfs:临时根文件系统
内核本身不知道你的硬盘是什么接口、根分区是什么文件系统。直接挂 / 必失败。解决方案是 initramfs(init ram filesystem):一个打包成 cpio.gz 的迷你根文件系统,里面放着常见磁盘驱动和工具。
流程:
- GRUB 把
vmlinuz内核和initramfs.img一起加载到内存。 - 内核先把 initramfs 挂成临时根,跑里面的
/init脚本。 /init探测硬件,加载真正的磁盘/文件系统驱动(比如你的根分区在 LVM 或 RAID 上,这里才挂上)。- 挂上真实根,
switch_root切过去,丢掉 initramfs。
重新生成 initramfs
加了新内核驱动、改了磁盘布局,都要重生成:
bash
sudo update-initramfs -u # Ubuntu/Debian
sudo dracut -f # CentOS/Rocky
不重生成,新内核或新驱动可能在启动阶段认不出根分区,直接 panic。
内核 panic 长什么样
text
Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
看到 Unable to mount root fs,基本就是 initramfs 里缺驱动,或者 /etc/fstab 里根分区 UUID 写错了。
五、systemd:PID 1
切到真实根之后,第一个被内核拉起来的用户态进程就是 /sbin/init,现在指向 systemd。它的 PID 永远是 1。
systemd 启动干这些事:
- 挂载
/etc/fstab里定义的所有文件系统。 - 初始化网络、主机名、时区。
- 按依赖关系启动 default target 下挂的所有服务。
看启动花了多久:
bash
systemd-analyze
预期输出:
text
Startup finished in 2.312s (kernel) + 1.847s (userspace) = 4.159s
graphical.target reached after 1.793s in userspace
哪个服务拖慢了启动:
bash
systemd-analyze blame
预期输出(节选):
text
852ms dnf-makecache.service
410ms systemd-journal-flush.service
233ms firewalld.service
102ms NetworkManager-wait-online.service
45ms sshd.service
NetworkManager-wait-online.service 常年霸榜,服务器不需要等网络完全就绪再启动应用,直接 sudo systemctl disable NetworkManager-wait-online 能省一两秒。
blame 只告诉你谁最慢,没说它在等谁。想看某条服务的完整等待链,用 critical-chain:
bash
systemd-analyze critical-chain multi-user.target
预期输出(节选):
text
multi-user.target @1.793s
└─ NetworkManager-wait-online.service @1.582s +102ms
└─ NetworkManager.service @1.204s +312ms
└─ network-pre.target @1.201s
缩进和箭头画出来的就是依赖等待关系:multi-user.target 等 NetworkManager-wait-online,它又等 NetworkManager。启动慢到底卡在哪一环,一眼看清,不用一个个服务猜。
六、常见故障排查入口
| 卡在哪 | 现象 | 先查什么 |
|---|---|---|
| 固件后黑屏 | 找不到启动设备 | 启动盘顺序、ESP 分区、MBR 是否被覆盖 |
| 卡在 GRUB 提示符 | grub> |
GRUB 没装对或 grub.cfg 找不到,进救援盘重装 |
| 内核 panic | VFS: Unable to mount root |
initramfs、fstab UUID、磁盘是否识别 |
| 一直卡在 A start job is running | 某服务超时 | GRUB 里加 systemd.unit=multi-user.target 跳过问题服务 |
| 能启动但服务不对 | 应用没起来 | 进系统后 journalctl -xb 看本次启动日志 |
进紧急模式的两种方式:GRUB 启动参数里加 systemd.unit=rescue.target 进救援模式,加 emergency.target 进最小化紧急 shell。救援模式至少会尝试挂文件系统,emergency 模式只挂只读根,排查 fstab 问题用 emergency。
七、知识扩展:从 MBR 到 ESP 的引导史
理解了 BIOS 和 UEFI 的差别,很多老文档的"MBR 第一扇区"说法才有意义。
Legacy BIOS 启动一台 Linux 机器,链条是这样的:
text
BIOS → 读 MBR(硬盘第一扇区 512 字节)
→ MBR 里的 GRUB boot.img(stage1,只有 446 字节)
→ 读 GRUB core.img(stage1.5,放 MBR 之后的间隙分区)
→ 加载 GRUB 内核(stage2)
→ 读 /boot/grub/grub.cfg
→ 加载 vmlinuz + initramfs
446 字节连个文件系统都读不懂,这就是为什么需要分段。stage1 唯一的任务就是把 stage2 找出来。
UEFI 把这一切省了。固件本身就懂 FAT 文件系统,ESP 分区上直接放一个 grubx64.efi 文件,固件按 NVRAM 里记的路径加载它,没有 MBR、没有 stage1、没有 boot 间隙。代价是 ESP 必须是 FAT32 分区,而且分区表得是 GPT。
怎么确认自己的 /boot 长啥样:
bash
ls /boot
预期输出(UEFI 机器):
text
config-6.8.0-45-generic initrd.img-6.8.0-45-generic System.map-6.8.0-45-generic
efi grub vmlinuz-6.8.0-45-generic
initrd.img grub2 vmlinuz
vmlinuz 是压缩后的内核,initrd.img 就是 initramfs。efi 目录挂载着 ESP。装内核时 apt install linux-image-xxx 或 dnf install kernel,包管理会自动把这两个文件扔进来并跑一遍 update-grub,你不用手动改 grub.cfg------知道这条流水线存在,排障时才知道该重跑哪一步。