3. GRUB 配置与调试:GRUB 命令行模式,内核参数传递(console, earlyprintk, debug),救援模式与单用户模式
GRUB(GRand Unified Bootloader)是 Linux 系统启动过程中第一个由用户直接交互的组件。当内核早期启动出现崩溃、挂起或控制台无输出时,GRUB 提供了关键的调试入口。本章聚焦于 GRUB 的命令行交互、内核参数传递技巧以及两种重要的故障恢复模式。
3.1 GRUB 命令行模式:启动前的交互式调试
GRUB 命令行模式允许用户在加载内核之前手动编辑启动项、查看设备信息或逐条执行 GRUB 命令。这是排查内核参数错误、根文件系统路径错误或硬件识别失败的第一道防线。
grub>。
常用 GRUB 调试命令:
| 命令 | 功能 | 调试场景 |
|---|---|---|
ls |
列出所有可识别的分区和设备 | 确认 GRUB 能否看到磁盘和分区 |
ls (hd0,1)/ |
列出指定分区根目录的文件 | 验证内核或 initrd 文件是否存在 |
set |
显示当前 GRUB 环境变量 | 检查 root、prefix 等变量是否正确 |
cat (hd0,1)/boot/grub/grub.cfg |
查看配置文件内容 | 确认菜单项的内核参数 |
linux |
加载内核并指定参数 | 手动指定内核路径和调试参数 |
initrd |
加载初始 RAM 磁盘 | 手动指定 initrd 路径 |
boot |
启动已加载的内核 | 执行手动配置的启动流程 |
ls (hd0,1)/boot/vm 后按 Tab,GRUB 会自动补全内核文件名,这有助于确认文件路径是否正确。
3.2 内核参数传递:console、earlyprintk 与 debug
内核参数是连接 GRUB 与内核早期调试的桥梁。通过向内核传递特定的参数,可以控制控制台输出设备、启用早期打印功能以及调整内核的调试级别。
3.2.1 console 参数:控制内核消息输出
console 参数用于指定内核日志的输出设备。可以指定多个 console,内核会向所有指定的设备输出消息。
# 将内核消息输出到串口 ttyS0,波特率 115200
console=ttyS0,115200
# 同时输出到串口和 VGA 文本控制台
console=ttyS0,115200 console=tty0
# 仅输出到 VGA 控制台(默认行为)
console=tty0
/dev/console)。如果串口和 VGA 同时启用,通常将串口放在前面用于调试,VGA 放在后面作为主控制台。
3.2.2 earlyprintk 参数:捕获早期内核崩溃
earlyprintk 是内核早期启动调试的“杀手锏”。它在内核初始化控制台子系统之前就启用了一个极简的打印机制,能够输出从 start_kernel() 到控制台初始化完成之间的关键日志。
# 输出到串口
earlyprintk=serial,ttyS0,115200
# 输出到 VGA 屏幕(仅 x86)
earlyprintk=vga
# 保持 early console 在正常 console 初始化后继续工作
earlyprintk=serial,ttyS0,115200 keep
earlyprintk=serial,ttyS0,115200 并通过串口线连接另一台机器捕获日志,往往是获取崩溃信息的唯一途径。
3.2.3 debug 参数:提升内核日志详细度
debug 参数是一个通用开关,它会将内核的日志级别提升到最高(KERN_DEBUG),并启用许多内核子系统的额外调试输出。
# 启用内核调试输出
debug
# 同时启用 earlyprintk 和 debug
earlyprintk=serial,ttyS0,115200 debug
# 指定日志级别(数字越小越紧急,7 为 debug 级别)
loglevel=7
参数组合对比:
| 参数组合 | 输出时间点 | 信息详细度 | 适用场景 |
|---|---|---|---|
console=tty0 |
控制台初始化后 | 默认(KERN_INFO 及以上) | 正常启动 |
earlyprintk=serial |
从 setup_arch() 开始 | 默认 | 早期挂起、无输出 |
earlyprintk=serial debug |
从 setup_arch() 开始 | 最高(KERN_DEBUG) | 内核早期崩溃、驱动初始化失败 |
earlyprintk=serial debug ignore_loglevel |
从 setup_arch() 开始 | 强制输出所有日志 | 严重崩溃,日志被抑制 |
3.3 救援模式与单用户模式
当系统无法正常启动到多用户模式时,GRUB 提供了两种特殊的启动目标:救援模式(rescue.target)和单用户模式(emergency.target / single)。
3.3.1 单用户模式(Single User Mode)
单用户模式启动一个具有 root 权限的最小化 shell,通常用于修复文件系统错误、重置 root 密码或禁用有问题的服务。
进入单用户模式的方法:
- 在 GRUB 菜单界面,选中目标内核条目,按下 e 键进入编辑模式。
- 找到以
linux开头的行,在行末添加single或1或S。 - 按下 Ctrl+x 或 F10 启动。
# 在 linux 行末添加
linux /vmlinuz-5.15.0 root=/dev/sda1 ro single
single 会被映射为 systemd.unit=rescue.target。也可以直接使用 systemd.unit=rescue.target 或 systemd.unit=emergency.target 来精确控制启动目标。
3.3.2 救援模式(Rescue Mode)
救援模式比单用户模式更底层。它启动一个最小的系统环境,挂载根文件系统为只读,不启动任何服务,甚至不启动网络。适用于根文件系统损坏、关键系统库缺失等严重故障。
救援模式与单用户模式对比:
| 特性 | 单用户模式(rescue.target) | 救援模式(emergency.target) |
|---|---|---|
| 根文件系统挂载 | 读写(rw) | 只读(ro) |
| 网络服务 | 不启动 | 不启动 |
| 多用户支持 | 否 | 否 |
| 文件系统检查 | 通常不执行 | 不执行 |
| 适用场景 | 服务配置错误、密码重置 | 根文件系统损坏、内核模块缺失 |
| 内核参数 | single 或 1 |
systemd.unit=emergency.target |
3.4 GRUB 调试与内核参数传递流程
以下 SVG 图展示了从 GRUB 菜单到内核启动的完整调试参数注入流程,以及不同参数生效的阶段。
3.5 实战案例:通过 GRUB 调试串口无输出问题
问题现象: 嵌入式设备启动后,串口终端没有任何输出,系统似乎挂死在 GRUB 加载内核之后。
排查步骤:
- 确认 GRUB 阶段正常: 在 GRUB 菜单按 c 进入命令行,执行
ls确认磁盘可见,执行cat (hd0,1)/boot/grub/grub.cfg确认配置文件可读。 - 注入 earlyprintk 参数: 按 e 编辑启动项,在
linux行末添加earlyprintk=serial,ttyS0,115200 debug ignore_loglevel。 - 捕获早期日志: 启动后,串口终端输出如下关键信息:
Early console on ttyS0 at 0x3f8 (options '115200') bootconsole [uart0] enabled [ 0.000000] Linux version 5.15.0 ... [ 0.000123] Kernel command line: ... earlyprintk=serial,ttyS0,115200 debug [ 0.000456] console [ttyS0] enabled [ 0.000789] bootconsole [uart0] disabled - 分析日志: 日志显示控制台在
[0.000456]秒时成功启用,但随后在某个驱动初始化时崩溃。通过debug参数提供的详细日志,定位到是8250串口驱动在探测时访问了错误的 MMIO 地址。 - 修复: 在 GRUB 参数中添加
8250.nr_uarts=1或修正设备树中的串口地址映射,系统正常启动。
earlyprintk 是唯一能在控制台初始化前输出信息的机制。配合 debug 和 ignore_loglevel,可以确保所有内核消息都被强制输出,不遗漏任何崩溃前的线索。