Skip to content

GRUB 引导程序 ​

固件把控制权交给磁盘后,真正把内核搬进内存、并把你调到的那行内核参数交给内核的,是引导加载器(bootloader)。Linux 世界里它几乎姓 GRUB——Debian/Ubuntu 与 RHEL/CentOS/Rocky 默认都是 GRUB2,Arch 则把选择权交给用户(GRUB、systemd-boot 或 rEFInd 都常见)。GRUB 是一个完整的小型操作系统:它有菜单、有命令行、能读 ext4/Btrfs 文件系统、能在 32 位保护模式下寻址,这正是它比当年 BIOS 时代那 446 字节的 stub 复杂得多的原因。

理解 GRUB 的关键在于分层:你平时改的是"源"(/etc/default/grub、/etc/grub.d/),系统执行的是"生成物"(grub.cfg),而引导失败时你要动的往往是更底层的"安装"(grub-install 写入 MBR 或 ESP)。把这三层混为一谈,就会出现"改了配置没生效"、"救回来了但菜单还是坏的"这类经典困境。本页从结构讲到配置,再给出一套三系通用的救援流程。

内容参考自 GNU GRUB 手册、Arch Wiki 与鸟哥的私房菜,见文末参考资料。

学习目标 ​

  • 分清 GRUB 的两级加载结构(BIOS 与 UEFI 路径差异)
  • 理解配置的三层来源,知道为什么不能直接编辑 grub.cfg
  • 掌握三系重新生成 GRUB 配置与重装 GRUB 的命令差异
  • 会用 GRUB 菜单按 e 临时修改内核参数(进救援/单用户模式的标准入口)
  • 掌握 Live USB + chroot 的完整引导救援流程,区分 BIOS 与 UEFI 的 grub-install 参数

1. GRUB 的两级结构 ​

BIOS/Legacy 路径 ​

  1. 第一级:boot.img——写在 MBR 前 446 字节。它只做一件事:定位并加载第二级。
  2. 第二级:core.img——通常紧随 MBR 之后的间隙,或放在 BIOS Boot 分区(GPT 上专为它留的小分区)。core.img 包含文件系统驱动,能读出 /boot/grub/i386-pc/ 下的模块,最终加载 normal.mod 并解析 grub.cfg。
  3. grub.cfg 读出来后,GRUB 展示菜单,选中项把内核(vmlinuz-*)与 initramfs(initrd.img-*)读入内存,跳转到内核入口。

UEFI 路径 ​

  1. 固件按 NVRAM 启动项找到 ESP 里的 .efi 文件(通常是 \EFI\<发行版>\grubx64.efi,或经 shimx64.efi 中转)。
  2. 该 .efi 文件本身就是完整的 GRUB(EFI 版),它读取 ESP 或 /boot/grub/ 下的 grub.cfg。
  3. 后续与 BIOS 路径一致:菜单 → 加载内核/initramfs → 内核启动。

两条路径的共同终点都是 grub.cfg 和内核镜像;分歧只在"谁把它读出来"。理解这一点,救援时就知道要分别修复:BIOS 要修 MBR/core.img,UEFI 要修 ESP 里的 .efi 文件与 NVRAM 条目。

bash
$ ls /boot/grub/     # Debian/Ubuntu、Arch;RHEL 系为 /boot/grub2/
grub.cfg  fonts  grubenv  locale  x86_64-efi/  i386-pc/

2. 配置的三层结构 ​

GRUB 的配置是生成式的,这也是新手最容易踩坑的地方:

层位置你该不该改
用户变量/etc/default/grub日常改这里(超时、默认项、内核参数)
菜单生成脚本/etc/grub.d/*进阶用(自定义菜单项、双系统探测)
最终配置/boot/grub/grub.cfg 或 /boot/grub2/grub.cfg永远不改,改了也会被覆盖

grub.cfg 文件头就写着 "DO NOT EDIT THIS FILE"——它由 grub-mkconfig 调用 grub.d 下的脚本、读取 /etc/default/grub 汇总生成。正确工作流永远是:改源 → 重新生成 → 验证。

bash
$ cat /etc/default/grub
GRUB_DEFAULT=0                  # 默认菜单项索引(0 = 第一个),也可写子菜单字符串
GRUB_TIMEOUT=5                  # 菜单停留秒数;0 则直接启动默认项(藏菜单)
GRUB_DISTRIBUTOR="Ubuntu"       # 菜单里发行版名字
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"   # 追加到正常启动项的参数
GRUB_CMDLINE_LINUX=""           # 追加到所有项(含 recovery)的参数
GRUB_DISABLE_OS_PROBER=false    # 是否探测 Windows/macOS 生成菜单项

GRUB_CMDLINE_LINUX_DEFAULT 与 GRUB_CMDLINE_LINUX 的区别:前者只作用于正常启动项(quiet splash 这类"日常参数"放这里),后者作用于所有项包括恢复模式(root=/dev/sda2 这类"硬性参数"由安装器自动写入)。

修改示例——把默认启动项改为第 2 个、关掉 quiet 便于排障:

bash
$ sudo sed -i 's/GRUB_DEFAULT=0/GRUB_DEFAULT=1/' /etc/default/grub
$ sudo sed -i 's/GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"/GRUB_CMDLINE_LINUX_DEFAULT=""/' /etc/default/grub

/etc/grub.d/ 下的脚本按文件名数字顺序执行,常见几个:

脚本作用
00_header读取 /etc/default/grub,生成通用设置(主题、超时、模块加载)
05_debian_theme背景图/字体(Debian/Ubuntu)
10_linux扫描 /boot 下的内核并生成 Linux 菜单项(最关键)
30_os-prober探测其他操作系统(Windows、其他发行版)
40_custom留给用户手写菜单项,每次重新生成都会被保留

3. 重新生成与重装(三系对照) ​

重新生成配置(改完 /etc/default/grub 后必做):

bash
# Debian/Ubuntu
$ sudo update-grub
Sourcing file `/etc/default/grub'
Found linux image: /boot/vmlinuz-6.8.0-45-generic
Found initrd image: /boot/initrd.img-6.8.0-45-generic
Found Windows Boot Manager on /dev/sda1@/EFI/Microsoft/Boot/bootmgfw.efi
done

# Arch(显式指定输出路径)
$ sudo grub-mkconfig -o /boot/grub/grub.cfg

# RHEL/CentOS/Rocky(二进制与路径都带 2)
$ sudo grub2-mkconfig -o /boot/grub2/grub.cfg

重装 GRUB 本身(引导代码损坏、换磁盘、chroot 救援时用)——这一步才是"写 MBR"或"写 ESP":

bash
# BIOS/Legacy:写入整块磁盘的 MBR(注意是磁盘,不是分区)
$ sudo grub-install /dev/sda

# UEFI:写入 ESP 并向 NVRAM 注册启动项
$ sudo grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB
#   --efi-directory:ESP 挂载点(Arch 若 ESP 挂 /boot 则改为 /boot)
#   --bootloader-id:ESP 下 EFI/ 后的目录名,也是 NVRAM 条目显示名

# RHEL 系(命令名带 2)
$ sudo grub2-install /dev/sda                                      # BIOS
$ sudo grub2-install --target=x86_64-efi --efi-directory=/boot/efi # UEFI

常见坑:

  1. UEFI 下漏写 --efi-directory——GRUB 会默认找 /boot/efi,若你的 ESP 挂在 /boot(Arch 常见布局)就会装错位置,重启后 NVRAM 指向的文件不存在。
  2. 把分区当磁盘传给 grub-install——BIOS 模式应传 /dev/sda(磁盘),不是 /dev/sda1(分区)。传错通常会报 this utility will not install grub to one of your platform firmware 或静默装到错误位置。
  3. 生成了 grub.cfg 但没 grub-install——只修了菜单、没修引导代码,开机依然停在 grub> 提示符或 no bootable device。
  4. Arch 上用 update-grub——该命令是 Debian 专属脚本,Arch 没有,直接 command not found。Arch 一律用 grub-mkconfig -o ...。

4. 临时修改内核参数(GRUB 菜单按 e) ​

不需要改配置文件、重启立即生效的排障入口:开机出现 GRUB 菜单时按 e 进入编辑模式,找到以 linux(或 linuxefi/linux16)开头的行,在行尾追加参数,然后按 Ctrl+X(或 F10)以修改后的参数启动。

text
linux   /boot/vmlinuz-6.8.0-45-generic root=UUID=... ro quiet splash $vt_handoff
# ↑ 光标移到行尾,空格追加 systemd.unit=rescue.target,再按 Ctrl+X 启动

常用救援参数:

参数用途
systemd.unit=rescue.target救援模式(单用户 + 网络关闭,需 root 密码)
systemd.unit=emergency.target紧急模式(最小环境,initramfs 阶段失败后用)
init=/bin/bash直接以 bash 当 PID 1(无需密码,改 fstab/sudoers 后补救)
systemd.unit=multi-user.target不启动图形界面(Xorg 驱动坏了时进 tty)
ro → rw强制以读写挂载根分区(只读导致改不了文件时)
rd.break(RHEL 系 initramfs)在挂载真实根之前停下,改 root 密码用

改 root 密码的标准路径(三系通用思路):GRUB 菜单按 e → 在 linux 行末尾加 rw init=/bin/bash → Ctrl+X 进 root shell → mount -o remount,rw /(若已是 rw 则跳过)→ passwd root → exec /sbin/init 或直接 reboot。这条链不依赖知道旧密码,也是物理接触机器时的安全边界来源——服务器机房锁好、BIOS 设密码,都是在补这层"物理 = 最高权限"的洞。

编辑完菜单后,这只是本次启动临时生效;要永久生效仍需改 /etc/default/grub 并重新生成。

5. 引导失败救援流程(Live USB + chroot) ​

适用场景:开机停在 grub> 提示符、error: no such partition、kernel panic - not syncing: VFS: Unable to mount root fs、或 NVRAM 里根本没有启动项。

三系共用的骨架如下,差异集中在 grub-install/生成配置的命令名与路径上:

bash
# ---- 1. Live USB 启动,确认磁盘布局 ----
$ lsblk -f              # 假设 sda1 = ESP(FAT32),sda2 = ext4 根分区

# ---- 2. 挂载根分区 ----
$ sudo mount /dev/sda2 /mnt

# ---- 3. 挂载 ESP(UEFI 救援必做;Arch 常见为 /mnt/boot)----
$ sudo mount /dev/sda1 /mnt/boot/efi

# ---- 4. 绑定虚拟文件系统 ----
$ sudo mount --bind /dev  /mnt/dev
$ sudo mount --bind /proc /mnt/proc
$ sudo mount --bind /sys  /mnt/sys
$ sudo mount --bind /run  /mnt/run

# ---- 5. 进入 chroot(此后 / 即原系统根)----
$ sudo chroot /mnt

# ---- 6a. BIOS:重装到磁盘 MBR(RHEL 系用 grub2-install)----
grub-install /dev/sda

# ---- 6b. UEFI:重装到 ESP(Arch 若 ESP 挂 /boot 则 --efi-directory=/boot)----
grub-install --target=x86_64-efi --efi-directory=/boot/efi --bootloader-id=GRUB

# ---- 7. 重新生成配置(按发行版选一条)----
update-grub                                # Debian/Ubuntu
# grub-mkconfig -o /boot/grub/grub.cfg     # Arch
# grub2-mkconfig -o /boot/grub2/grub.cfg   # RHEL/CentOS/Rocky

# ---- 8. 内核也损坏时重建 initramfs(三系各一条)----
# update-initramfs -u / mkinitcpio -P / dracut --force

# ---- 9. 退出并重启 ----
exit
sudo umount -R /mnt
sudo reboot

判断走 6a 还是 6b:回到第 0 步——Live 环境里 ls /sys/firmware/efi 存在说明固件是 UEFI,对应 6b;不存在则对应 6a。另可看 ESP 是否存在(有 c12a7328-... 类型的 FAT 分区基本就是 UEFI)。

只修 NVRAM、不重写文件:如果 efibootmgr -v 显示 GRUB 条目还在,只是顺序乱了,根本不用 chroot,直接 sudo efibootmgr -o 调顺序即可。救援的复杂度应与损坏层级匹配——先用只读命令定位,再决定动哪一层。

6. 三系差异速览 ​

主题Debian/UbuntuArchRHEL/CentOS/Rocky
默认 bootloaderGRUB2用户选择(GRUB / systemd-boot / rEFInd)GRUB2
配置源/etc/default/grub、/etc/grub.d/同左(GRUB);systemd-boot 用纯文本 entry 文件/etc/default/grub、/etc/grub.d/
生成的配置/boot/grub/grub.cfg/boot/grub/grub.cfg/boot/grub2/grub.cfg
重新生成sudo update-grubsudo grub-mkconfig -o /boot/grub/grub.cfgsudo grub2-mkconfig -o /boot/grub2/grub.cfg
重装(BIOS)grub-install /dev/sdagrub-install /dev/sdagrub2-install /dev/sda
重装(UEFI)grub-install --target=x86_64-efi ...同左,注意 --efi-directorygrub2-install --target=x86_64-efi ...
典型 ESP 挂载点/boot/efi/boot(ESP 即 /boot,Wiki 推荐)/boot/efi
initramfs 工具update-initramfsmkinitcpiodracut
双系统菜单探测os-prober(update-grub 自动调用)需自行 pacman -S os-prober 并在 grub.d 启用os-prober(grub2-mkconfig 自动调用)

7. 常见坑 ​

  1. 改了 /etc/default/grub 但菜单没变。忘记重新生成。改源永远只是第一步,update-grub / grub-mkconfig / grub2-mkconfig 才是"提交"。

  2. 直接编辑 grub.cfg。下次重新生成会被覆盖,且文件头已警告。改源或改 40_custom。

  3. 救援时 grub-install 报找不到 EFI 变量或目录。多半是 chroot 后 ESP 没挂、或 --efi-directory 路径与实际挂载点不符。先在 chroot 里 mount | grep efi、ls /boot/efi 确认。

  4. Arch 上 ESP 挂成 /boot,却照抄 Debian 的 /boot/efi 路径。两处都要对齐:挂载点、--efi-directory、grub-mkconfig -o 的输出路径。

  5. GRUB_TIMEOUT=0 导致看不到菜单、进不了救援参数入口。开机时疯狂按 Shift(BIOS)或 Esc(部分 UEFI/GRUB 构建)可强制弹出菜单;或临时 GRUB_TIMEOUT=3 重新生成。

  6. 只跑了 grub-install 没跑 grub-mkconfig(或反过来)。前者修"引导器本体",后者修"菜单内容";引导失败往往两层都有问题,救援流程里两步都要执行。

  7. Btrfs 根分区 + 多级子卷时 root= 指向错误。grub.cfg 里 root=UUID=... 必须指向子卷(如 @),不是顶层设备。用 cat /etc/fstab 对照确认,救援时这一步错了内核会 panic 在挂根阶段。

参考资料 ​

相关页面 ​

基于 Apache License 2.0 许可发布