强曰为道

与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

第 5 章:自定义菜单项

第 5 章:自定义菜单项

5.1 为什么需要自定义菜单

虽然 grub-mkconfig 可以自动检测大多数操作系统,但以下场景需要手动添加菜单项:

  • 引导 Windows(os-prober 偶尔失灵)
  • 引导 USB Live 系统
  • 引导特定内核版本(编译的自定义内核)
  • 引导其他发行版的 ISO 镜像
  • 链式加载其他引导加载程序
  • 故障恢复时手动指定引导参数

5.2 自定义菜单项的位置

5.2.1 方法一:/etc/grub.d/40_custom(推荐)

这是官方推荐的自定义方式,文件内容在 update-grub 时不会被覆盖。

#!/bin/sh
exec tail -n +3 $0
# 此行以上的部分不会出现在 grub.cfg 中

# 你的自定义菜单项从这里开始
menuentry "My Custom Entry" {
    ...
}

5.2.2 方法二:/boot/grub/custom.cfg

通过 41_custom 加载,无需修改 40_custom

# 创建或编辑文件
$ sudo nano /boot/grub/custom.cfg

# 添加菜单项
menuentry "Rescue System" {
    ...
}

⚠️ 注意custom.cfg 不会参与 update-grub 流程,直接写入 /boot/grub/,适合快速测试。

5.2.3 方法三:GRUB Shell 临时引导

不修改任何文件,在 GRUB 启动时按 c 进入命令行,手动输入引导命令。详见第 8 章

5.3 引导 Windows

5.3.1 UEFI 模式引导 Windows

menuentry "Windows 11" --class windows --class os --unrestricted {
    # 加载分区模块
    insmod part_gpt
    insmod fat

    # 设置 Windows EFI 分区(通常是第一个分区)
    # 使用 UUID 更可靠(通过 blkid 获取)
    search --no-floppy --fs-uuid --set=root XXXX-XXXX

    # 链式加载 Windows Boot Manager
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

查找 Windows EFI 分区的 UUID:

$ sudo blkid | grep -i fat
/dev/sda1: UUID="ABCD-1234" TYPE="vfat" PARTLABEL="EFI" PARTUUID="xxxx-xxxx-xxxx"

# 或者更精确地查找 Windows EFI
$ sudo blkid | grep "PARTLABEL=\"EFI system partition\""

5.3.2 Legacy BIOS 模式引导 Windows

menuentry "Windows 10 (Legacy)" --class windows --class os {
    insmod part_msdos
    insmod ntfs
    insmod ntldr

    # Windows 安装在第一个分区
    set root='hd0,msdos1'

    # 方式一:通过 bootmgr 链式加载
    chainloader +1
    # 方式二:直接加载 bootmgr(推荐)
    # ntldr /bootmgr
}

5.3.3 Windows 在不同磁盘上

# Windows 在第二块磁盘的 EFI 分区
menuentry "Windows 11 (Disk 2)" --class windows {
    insmod part_gpt
    insmod fat
    search --no-floppy --fs-uuid --set=root YYYY-YYYY
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

5.4 引导其他 Linux 发行版

5.4.1 引导其他磁盘上的 Linux

menuentry "Ubuntu 24.04 on /dev/sdb2" --class gnu-linux {
    insmod part_gpt
    insmod ext2

    # 设置目标 Linux 的 /boot 分区
    set root='hd1,gpt2'

    # 使用 UUID 更可靠
    search --no-floppy --fs-uuid --set=root yyyy-yyyy-yyyy

    # 加载内核和 initramfs
    linux /vmlinuz root=UUID=zzzz-zzzz-zzzz ro quiet
    initrd /initrd.img
}

5.4.2 从 ISO 文件引导

这是一个非常实用的功能,可以直接从 ISO 镜像引导 Live 系统。

引导 Ubuntu ISO:

menuentry "Ubuntu 24.04 Live ISO" {
    insmod loopback
    insmod iso9660
    insmod part_gpt
    insmod ext2

    # ISO 文件所在的分区
    set root='hd0,gpt4'

    # 挂载 ISO 为回环设备
    loopback loop /iso/ubuntu-24.04-desktop-amd64.iso

    # 从 ISO 中加载内核
    linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/iso/ubuntu-24.04-desktop-amd64.iso quiet splash
    initrd (loop)/casper/initrd
}

引导 Debian ISO:

menuentry "Debian 12 Netinst ISO" {
    insmod loopback
    insmod part_gpt
    insmod ext2

    set root='hd0,gpt4'
    loopback loop /iso/debian-12-amd64-netinst.iso

    linux (loop)/install.amd/vmlinuz iso-scan/filename=/iso/debian-12-amd64-netinst.iso
    initrd (loop)/install.amd/initrd.gz
}

引导 Fedora ISO:

menuentry "Fedora 39 Workstation ISO" {
    insmod loopback
    insmod part_gpt
    insmod ext2

    set root='hd0,gpt4'
    loopback loop /iso/Fedora-Workstation-Live-x86_64-39-1.5.iso

    linux (loop)/images/pxeboot/vmlinuz root=live:CDLABEL=Fedora-WS-Live-39-1-5 iso-scan/filename=/iso/Fedora-Workstation-Live-x86_64-39-1.5.iso
    initrd (loop)/images/pxeboot/initrd.img
}

⚠️ 注意:ISO 引导的内核参数因发行版而异,需要参考各发行版的文档。ISO 文件需要存放在 GRUB 可识别的文件系统分区上。

5.4.3 引导自定义编译的内核

menuentry "Custom Kernel 6.8.0-custom" {
    insmod part_gpt
    insmod ext2
    set root='hd0,gpt2'

    linux /vmlinuz-6.8.0-custom root=UUID=xxx ro quiet
    initrd /initrd.img-6.8.0-custom
}

5.5 链式加载(Chainloading)

链式加载是将引导控制权交给另一个引导加载程序的技术。

5.5.1 链式加载的工作原理

GRUB2 → chainloader 命令 → 目标引导程序 → 操作系统

chainloader 命令将指定位置的代码加载到内存并执行,相当于"转交引导权"。

5.5.2 链式加载 EFI 程序

# 链式加载任何 EFI 程序
menuentry "EFI Shell" {
    insmod part_gpt
    insmod fat
    set root='hd0,gpt1'
    chainloader /EFI/tools/shell.efi
}

# 链式加载另一个 GRUB
menuentry "Another GRUB Installation" {
    insmod part_gpt
    insmod fat
    set root='hd0,gpt1'
    chainloader /EFI/ubuntu/grubx64.efi
}

# 链式加载 rEFInd
menuentry "rEFInd Boot Manager" {
    insmod part_gpt
    insmod fat
    set root='hd0,gpt1'
    chainloader /EFI/refind/refind_x64.efi
}

5.5.3 链式加载 MBR(Legacy BIOS)

# 从第二块磁盘的 MBR 链式加载
menuentry "Boot from second disk" {
    set root=(hd1)
    chainloader +1    # +1 表示加载第一个扇区(MBR)
}

# 从分区的引导扇区链式加载
menuentry "Boot partition /dev/sdb1" {
    set root=(hd1,msdos1)
    chainloader +1
}

5.6 引导 ISO 镜像的进阶技巧

5.6.1 通用 ISO 引导模板

menuentry "Boot ISO: <ISO名称>" {
    # 1. 加载必要模块
    insmod loopback
    insmod part_gpt
    insmod ext2

    # 2. 设置 ISO 文件所在分区
    search --set=root --file /iso/<文件名>.iso

    # 3. 创建回环设备
    loopback loop /iso/<文件名>.iso

    # 4. 从 ISO 加载内核(路径因发行版而异)
    linux (loop)/<内核路径> <引导参数>
    initrd (loop)/<initrd路径>
}

5.6.2 Memdisk 引导 ISO(BIOS 模式)

对于某些小 ISO,可以使用 memdisk 将整个 ISO 加载到内存:

menuentry "Hiren's BootCD" {
    insmod part_msdos
    insmod fat
    set root='hd0,msdos1'
    linux16 /memdisk iso
    initrd16 /HBCD_PE_x64.iso
}

⚠️ 注意:memdisk 方式会将整个 ISO 加载到 RAM,ISO 文件不能太大(建议 < 2GB),且需要足够内存。

5.7 高级菜单组织

5.7.1 使用 submenu 组织菜单

submenu "Tools and Utilities" --class tools {
    menuentry "Memtest86+" {
        insmod part_gpt
        insmod ext2
        set root='hd0,gpt2'
        linux /boot/memtest86+x64.bin
    }

    menuentry "SystemRescue" {
        insmod part_gpt
        insmod ext2
        search --set=root --file /iso/systemrescue.iso
        loopback loop /iso/systemrescue.iso
        linux (loop)/sysresccd/boot/x86_64/vmlinuz archisobasedir=sysresccd img_label=SYSRES img_loop=/iso/systemrescue.iso
        initrd (loop)/sysresccd/boot/x86_64/sysresccd.img
    }

    menuentry "GParted Live" {
        insmod loopback
        insmod part_gpt
        insmod ext2
        set root='hd0,gpt4'
        loopback loop /iso/gparted-live.iso
        linux (loop)/live/vmlinuz boot=live components config quiet
        initrd (loop)/live/initrd.img
    }
}

5.7.2 使用 hotkey 设置快捷键

menuentry "Windows" --hotkey='w' --class windows {
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

menuentry "Ubuntu" --hotkey='u' --class gnu-linux {
    linux /boot/vmlinuz root=UUID=xxx ro
    initrd /boot/initrd.img
}

5.7.3 密码保护特定菜单项

# 在 /etc/grub.d/40_custom 中
# 需要先在 /etc/grub.d/00_header 或 40_custom 中设置密码

menuentry "Admin Recovery" --users admin {
    insmod part_gpt
    insmod ext2
    set root='hd0,gpt2'
    linux /boot/vmlinuz root=UUID=xxx ro single
    initrd /boot/initrd.img
}

5.8 完整自定义示例

以下是一个完整的 /etc/grub.d/40_custom 示例:

#!/bin/sh
exec tail -n +3 $0

# ==================================================
# Windows 引导
# ==================================================
menuentry "Windows 11" --class windows --class os --unrestricted {
    insmod part_gpt
    insmod fat
    search --no-floppy --fs-uuid --set=root ABCD-1234
    chainloader /EFI/Microsoft/Boot/bootmgfw.efi
}

# ==================================================
# Ubuntu 24.04 ISO
# ==================================================
menuentry "Ubuntu 24.04 Desktop ISO" --class ubuntu {
    insmod loopback
    insmod part_gpt
    insmod ext2
    search --no-floppy --fs-uuid --set=root 1111-2222-3333
    loopback loop /iso/ubuntu-24.04-desktop-amd64.iso
    linux (loop)/casper/vmlinuz boot=casper iso-scan/filename=/iso/ubuntu-24.04-desktop-amd64.iso quiet splash
    initrd (loop)/casper/initrd
}

# ==================================================
# 子菜单:工具
# ==================================================
submenu "System Tools" --class tools {
    menuentry "Memtest86+" {
        insmod part_gpt
        insmod ext2
        set root='hd0,gpt2'
        linux /boot/memtest86+x64.bin
    }

    menuentry "EFI Shell" {
        insmod part_gpt
        insmod fat
        set root='hd0,gpt1'
        chainloader /EFI/tools/shell.efi
    }
}

5.9 GRUB 模块参考

自定义菜单项时可能需要加载各种模块:

模块用途
part_gptGPT 分区表支持
part_msdosMBR 分区表支持
ext2ext2/3/4 文件系统支持
fatFAT12/16/32 文件系统支持
ntfsNTFS 文件系统支持
iso9660ISO 9660(CD-ROM)文件系统支持
loopback回环设备支持(挂载 ISO)
chainloader链式加载支持
search搜索分区/文件
linuxLinux 内核加载
all_video所有视频驱动
gziogzip 解压(用于压缩的内核/initrd)
test条件测试命令
lsefi列出 EFI 系统信息(仅 UEFI)
lspci列出 PCI 设备
btrfsBtrfs 文件系统支持
xfsXFS 文件系统支持
zfsZFS 文件系统支持

检查模块是否可用

# 在 GRUB Shell 中
grub> insmod part_gpt
# 如果无输出,说明加载成功
# 如果报错 "unknown filesystem",说明模块不存在

# 列出可用模块
grub> ls /boot/grub/x86_64-efi/

5.10 扩展阅读


上一章:第 4 章:自动生成配置 | 下一章:第 6 章:主题与美化