4. Bootloader 原理:GRUB2 的配置与启动菜单定制
Bootloader 是系统启动的“第一道关卡”。说白了,它负责把内核从磁盘加载到内存,然后跳过去执行。我见过不少开发者在应用层调优做得风生水起,但一提到 GRUB2 就两眼一抹黑。其实搞懂它,很多启动慢的问题就迎刃而解了。
4.1 GRUB2 的启动阶段
GRUB2 的启动过程分三个阶段。每个阶段都有明确的任务。我个人习惯把这三个阶段比作“接力跑”——每一棒只干自己该干的事,跑完就交棒。
| 阶段 | 所在位置 | 核心任务 | 我踩过的坑 |
|---|---|---|---|
| boot.img | MBR(主引导记录) | 加载 core.img 的前 512 字节 | MBR 被覆盖,系统直接黑屏 |
| core.img | MBR 之后的扇区 | 识别文件系统,加载 /boot/grub | 分区表错位,core.img 读不到 |
| grub.cfg | /boot/grub/ 目录 | 解析配置,显示菜单,加载内核 | 语法写错,菜单直接不显示 |
为什么会这样设计?因为 MBR 只有 512 字节,根本塞不下完整的文件系统驱动。所以 GRUB2 用了一个“拆解”的思路:先让最小的 boot.img 启动,再由它去拉 core.img,最后 core.img 才有能力去读 /boot 分区上的配置文件。
关键认知:GRUB2 的启动速度瓶颈往往不在 boot.img,而在 core.img 加载文件系统驱动的阶段。如果你用的是老旧的文件系统(比如 ext2),那读取速度会明显拖后腿。
4.2 grub.cfg 配置深度解析
grub.cfg 是 GRUB2 的核心配置文件。它通常由 grub-mkconfig 自动生成,但手动修改能带来更大的灵活性。嗯,这里要注意:自动生成的配置会覆盖手动修改,所以定制时最好在 /etc/default/grub 里改,或者直接写自定义菜单项。
4.2.1 常用配置项
| 配置项 | 作用 | 我推荐的设置 |
|---|---|---|
GRUB_DEFAULT |
默认启动项 | 0 或 "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic" |
GRUB_TIMEOUT |
菜单等待秒数 | 生产环境设 2,调试时设 10 |
GRUB_CMDLINE_LINUX |
传给内核的参数 | 加 quiet splash 隐藏启动日志 |
GRUB_GFXPAYLOAD_LINUX |
内核使用的分辨率 | 1920x1080 或 auto |
GRUB_DISABLE_RECOVERY |
是否生成恢复模式 | true(减少菜单项数量) |
我的小技巧:调试内核时,把 GRUB_CMDLINE_LINUX 里加上 earlyprintk=vga,keep loglevel=8,这样能第一时间看到内核崩溃的完整信息。我曾经靠这个参数定位过一个诡异的 ACPI 中断冲突问题。
4.2.2 手动添加自定义菜单项
有时候自动生成的菜单不够用。比如你想启动一个备用内核,或者直接进入 memtest86。这时候就需要手写菜单项了。
menuentry "My Custom Kernel - 6.1.0" {
set root=(hd0,msdos1)
linux /vmlinuz-6.1.0 root=/dev/sda1 ro quiet
initrd /initrd.img-6.1.0
}
这段代码里,set root 指定了 /boot 所在的分区。linux 行指定内核文件路径和根分区参数。initrd 行指定初始内存盘。我建议你写完后用 grub2-mkconfig -o /boot/grub2/grub.cfg 重新生成一下,确保语法正确。
避坑指南:我曾经在 set root 里写错了分区号,结果启动时直接 kernel panic。记住:hd0 是第一块磁盘,msdos1 是第一个分区。如果你用的是 GPT 分区表,要写成 gpt1 而不是 msdos1。
4.3 启动菜单定制实战
定制启动菜单不只是为了好看。合理的菜单设计能减少误操作,还能加快启动速度。你想想看,如果菜单里有 20 个选项,每次都要手动选,那多浪费时间。
4.3.1 减少菜单项数量
默认情况下,GRUB2 会为每个内核版本生成多个菜单项(普通模式、恢复模式等)。我建议只保留最新的两个版本,其他的删掉。
# 在 /etc/default/grub 中添加
GRUB_DISABLE_RECOVERY="true"
# 只保留最近两个内核
sudo apt autoremove --purge
sudo update-grub
4.3.2 设置默认启动项
生产环境里,我习惯把默认启动项设为一个稳定的内核版本。这样即使有人重启服务器,也不会因为选错菜单而出问题。
# 查看所有菜单项
grep menuentry /boot/grub/grub.cfg
# 设置默认启动项为第一个
GRUB_DEFAULT=0
# 或者用子菜单名称
GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic"
4.3.3 美化菜单界面
虽然 GRUB2 是文本界面的,但也能做一点美化。比如设置背景图片、调整字体大小。我个人习惯用一张 1024x768 的纯色背景图,这样看起来干净,而且加载快。
# 在 /etc/default/grub 中添加
GRUB_BACKGROUND="/boot/grub/background.png"
GRUB_GFXMODE="1024x768"
GRUB_FONT="/boot/grub/fonts/unicode.pf2"
注意:背景图片必须是 PNG 格式,分辨率不要太大。我曾经用了一张 4K 图片,结果 GRUB2 加载它花了 3 秒多,得不偿失。
4.4 GRUB2 启动流程全景图
下面这张图展示了 GRUB2 从 MBR 到内核加载的完整流程。我把它画出来,方便你对照理解。
4.5 常见问题与排查方法
GRUB2 出问题时,系统往往连菜单都看不到。我总结了几种常见场景和对应的排查思路。
| 现象 | 可能原因 | 我的排查步骤 |
|---|---|---|
| 黑屏,光标闪烁 | MBR 损坏或 core.img 丢失 | 用 Live CD 启动,执行 grub2-install /dev/sda |
| 菜单显示但无法启动 | grub.cfg 语法错误 | 按 e 编辑菜单项,检查路径和参数 |
| 启动后 kernel panic | 根分区参数错误 | 检查 root= 参数是否指向正确的分区 |
| 菜单加载极慢 | 背景图片过大或文件系统碎片化 | 换小图片,或者 fsck 修复文件系统 |
我曾经遇到过一个特别坑的情况:服务器重启后直接黑屏,连 BIOS 自检都过不去。查了半天发现是 MBR 被另一个系统的安装程序覆盖了。从那以后,我每次装多系统都会先备份 MBR:dd if=/dev/sda of=/backup/mbr.bin bs=512 count=1。这个习惯救过我两次。
4.6 性能优化建议
GRUB2 本身的启动时间通常只有几百毫秒,但配置不当会拖到几秒。我整理了几个优化方向,你可以根据实际情况选择。
- 减少 core.img 大小:只保留必要的文件系统模块。比如只用 ext4,就把其他模块删掉。
- 精简 grub.cfg:删除旧内核的菜单项,只保留 2-3 个。
- 调整 GRUB_TIMEOUT:生产环境设 1-2 秒,调试时再改大。
- 使用 SSD:如果 /boot 在机械硬盘上,换成 SSD 能明显加快 core.img 的加载速度。
- 禁用不必要的模块:比如
grub2-mkconfig默认会加载很多驱动,手动指定需要的模块能减少加载时间。
我的实测数据:在一台老服务器上,把 /boot 从 HDD 迁移到 SSD 后,GRUB2 的启动时间从 2.8 秒降到了 0.4 秒。这个优化效果比调内核参数还明显。
GRUB2 虽然看起来复杂,但只要理解了它的三个阶段和配置文件结构,定制起来并不难。记住:少即是多——精简的配置不仅启动快,还更容易排查问题。