Ubuntu修改GRUB默认启动内核全攻略:原理、配置与避坑指南
2026/9/16 18:25:28 网站建设 项目流程

系统里装了好几个内核版本,结果每次开机都要在GRUB菜单里盯着倒数计时,手忙脚乱地选内核,这种经历我太熟悉了。被逼到这个份上,多半是因为新内核和NVIDIA驱动打架、某个硬件模块只在旧内核下工作正常,或者跑实时内核(PREEMPT_RT)需要锁定版本。不管你是哪种情况,解决思路都落在同一个地方:GRUB配置。这篇文章我会把Ubuntu下修改GRUB默认启动内核的几种方案、底层原理和实操中踩过的坑一次性讲透,从查看菜单到调整配置再到验证,照着做就行。

1. 为什么需要修改默认启动内核

1.1 常见的多内核共存场景

先说说我在实际中遇到最多的情况。Ubuntu每次系统更新都会自动升级内核版本,默认会在GRUB菜单里保留旧内核,方便出问题时回退。但这样一来,GRUB菜单里往往有好几行"Ubuntu"和"Advanced options for Ubuntu",默认启动的是最新内核。

什么情况下你需要手动修改默认内核?根据我的经验,下面几类场景最常见:

  • 新内核和显卡驱动不兼容。装了NVIDIA专有驱动后某次内核升级,驱动模块编译失败,开机进桌面直接黑屏或分辨率异常,把默认内核指回旧版本是最快的止损方式。
  • 工控机、服务器需要固定内核版本。跑采集卡、运动控制卡、EtherCAT主站这类依赖内核模块的应用,必须用经过验证的内核版本,不能放任内核自动升级。这也是"实时内核"场景下最标准的操作。
  • 新内核存在回归问题。某些内核版本在特定硬件平台上的休眠唤醒、网络驱动、USB控制器方面会有反复,这时手动锁定旧内核是比较理性的选择。
  • 测试内核。你想试用候选版本,但希望系统默认还是走稳定内核,出问题随时回退。

提示:在修改默认内核之前,我强烈建议先通过GRUB菜单手动启动一次目标内核,确认它在你的机器上工作正常,再把它设为默认。跳过这一步,很容易把自己锁在门外。

1.2 GRUB在启动流程中的角色

GRUB(GRand Unified Bootloader)是Linux系统中最常用的引导加载程序。它的职责简单说就是:按下电源键之后,BIOS/UEFI从磁盘上把GRUB代码加载进来,GRUB读取/boot/grub/grub.cfg配置文件,然后把内核镜像(vmlinuz)和initrd加载到内存,接着把控制权交给内核。整个引导过程中,GRUB菜单项决定了加载哪个内核、以什么参数加载。

这里有一个很多新手会混淆的点:/etc/default/grub是用户编辑的模板配置,而/boot/grub/grub.cfg是GRUB启动时真正读取的运行时配置。我们每次运行update-grub或grub-mkconfig,系统会把/etc/default/grub里的设置、/etc/grub.d/目录下的脚本,以及当前系统中已安装的内核信息合并起来,重新生成一份grub.cfg。所以修改默认内核的完整链路是:改/etc/default/grub → 执行update-grub → 重启。这条链路是整个操作的主线,后边所有方案都围绕它展开。

2. 动手前的准备:看清系统现状

2.1 确认当前内核与候选内核

操作之前,先搞清楚系统里都有哪些内核。我的习惯是先看当前正在用的:

uname -r

输出类似5.15.0-91-generic,这就是当前内核版本。然后看系统里安装了哪些内核镜像包:

dpkg --list | grep linux-image

如果你看到linux-image-5.15.0-91-generic、linux-image-5.15.0-92-generic这些条目,就说明系统里装了多个内核。另外还要注意linux-headers包,那是编译内核模块时用的头文件,一般不影响启动顺序,但删除内核时建议一起清理。

也可以查看/boot目录:

ls -lh /boot/vmlinuz-*

这个命令会把所有内核镜像文件列出来,附带文件大小和修改时间,看起来更直观。我个人更习惯用dpkg来查,因为dpkg能同时告诉你内核包的状态(ii表示已安装)和架构,这个细节对于需要确认软件包来源、准备做双系统清理的人来说非常实用。

2.2 查看GRUB菜单的真实顺序

修改默认内核,最关键的一步是搞清楚GRUB菜单里每一项的真实排列。GRUB菜单项的序号从0开始,第一个菜单项是0,第二个是1,以此类推。

查看菜单项最可靠的方式是直接读grub.cfg:

grep -E "^menuentry|^submenu" /boot/grub/grub.cfg

这条命令会把所有顶层菜单项和子菜单项列出来。Ubuntu的GRUB菜单结构通常长这样:

  • Ubuntu(顶层菜单项)
  • Advanced options for Ubuntu(顶层子菜单)
    • Ubuntu, with Linux 5.15.0-92-generic
    • Ubuntu, with Linux 5.15.0-92-generic (recovery mode)
    • Ubuntu, with Linux 5.15.0-91-generic
    • Ubuntu, with Linux 5.15.0-91-generic (recovery mode)
    • ...

注意,Ubuntu默认的第一项"Ubuntu"实际上是指向"Advanced options for Ubuntu"子菜单里最新内核那条的,而不是一个简单的独立菜单项。这一个细节在后边用序号方式设置默认值的时候特别容易踩坑。

还有个更便于脚本处理的命令:

awk -F\' '/menuentry |submenu / {print $1$2}' /boot/grub/grub.cfg

这条命令用单引号作为分隔符,把每个菜单项类型和完整名称提取出来,输出结果更方便人工对照。前后两条命令建议都试一下,选择自己看着顺眼的方式。

2.3 理解GRUB配置体系

修改默认启动项,日常接触的核心是/etc/default/grub这个文件。打开看一下:

sudo cat /etc/default/grub

典型的内容是这样:

GRUB_DEFAULT=0 GRUB_TIMEOUT_STYLE=hidden GRUB_TIMEOUT=10 GRUB_DISTRIBUTOR=`lsb_release -i -s 2> /dev/null || echo Debian` GRUB_CMDLINE_LINUX_DEFAULT="quiet splash" GRUB_CMDLINE_LINUX=""

这几个变量里,跟本文主题关系最密切的是GRUB_DEFAULT,它控制默认启动哪个菜单项。GRUB_TIMEOUT控制菜单等待时间,GRUB_TIMEOUT_STYLE=hidden表示不显示菜单,直接按超时选择默认项。

理解了GRUB_DEFAULT的几种取值方式,后面才能真正"按需修改":

  • 数字:GRUB_DEFAULT=0代表第一个菜单项
  • 菜单项名称:GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic"
  • saved:配合GRUB_SAVEDEFAULT=true,用grub-set-default来指定

对于大多数固定场景,我建议优先用菜单项名称或saved方式;数字方式只在临时改动时用。

3. 修改默认内核的几种实用方案

3.1 方案一:GRUB_DEFAULT指定菜单项序号

最直接的思路:GRUB_DEFAULT=0默认是第一个菜单项,那我改成目标内核在菜单里的序号不就行了?

比如上面的例子,我想默认启动5.15.0-91-generic,它在子菜单里的序号是2(因为第0项是5.15.0-92,第1项是它的recovery mode,第2项才是5.15.0-91)。那么:

GRUB_DEFAULT="1>2"

这里1表示第二行菜单"Advanced options for Ubuntu"子菜单的序号,2表示子菜单内第三条,也就是5.15.0-91-generic。中间的大于号是分隔符,用来表示进入子菜单。

注意:这个写法有个隐藏的坑。如果你只写一个数字2,GRUB会把它解释为顶层菜单的第三项,而顶层一般只有"Ubuntu"和"Advanced options for Ubuntu"两项,根本不指向你想启动的实际内核。所以在有子菜单的情况下,"父菜单序号>子菜单序号"是必须完整的写法,序号之间的符号是英文半角的大于号>,不要写成别的字符。

这种用序号的方式最大的问题就是可读性差,而且一旦内核升级,GRUB菜单项的排列顺序变化,序号就乱了。所以对于要长期保持的配置,我不太推荐用序号方式,它更适合临时改一下、确认没问题就改回来的场景。

3.2 方案二:通过菜单项名称锁定内核

我更推荐的方式是用菜单项的完整名称来指定默认内核。在/etc/default/grub里这样写:

GRUB_DEFAULT="Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic"

这里的>用来分隔顶级菜单和子菜单,后面的字符串必须和grub.cfg里的menuentry名称完全一致。用名称锁定有一个好处:即使系统里又装了一个新内核,只要目标内核还在,这个字符串就不会变,默认项不会发生漂移。

有个小技巧:如果你嫌手敲名称容易拼错,可以先从grub.cfg里把名称复制出来:

grep "menuentry " /boot/grub/grub.cfg | cut -d "'" -f2

把输出里那一长串带"with Linux"的文本直接粘到配置文件里,再手动加上前面"Advanced options for Ubuntu>"前缀即可。注意双引号内不要多出空格,也不要手动加引号之外的特殊字符,否则GRUB匹配不到会回退到第一个菜单项。

3.3 方案三:saved方式配合grub-set-default

第三种方式适合"我可能频繁切换不同内核版本"的场景。原理很简单:GRUB会把"上次选择启动的菜单项"记录下来,以后默认启动上次选择的那一项。具体配置:

/etc/default/grub里设置两行:

GRUB_DEFAULT=saved GRUB_SAVEDEFAULT=true

然后更新配置:

sudo update-grub

重启之后,GRUB菜单会记住你开机时手动选择的内核,并把它的序号写入/boot/grub/grubenv文件,下一次开机默认启动的就是它。如果你想不通过菜单,直接在命令行里指定默认项,可以用:

sudo grub-set-default "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic"

这个命令会把默认项写入grubenv,效果等同于手动在菜单里选了那一项。saved方式的优点在于灵活,特别适合调试内核的场景:你可以从旧内核启动,测试完再手动引导新内核,系统会记住你最后一次的选择。缺点是你必须在每次重启后注意确认GRUB菜单里选对了项,不然默认项可能被意外修改。

3.4 方案四:临时切换用grub-reboot

有一种更"轻"的操作,不需要修改任何配置文件,只需要这一次开机用某个内核,之后还是用原来的默认项。执行:

sudo grub-reboot "Advanced options for Ubuntu>Ubuntu, with Linux 5.15.0-91-generic" sudo reboot

grub-reboot只会影响下一次启动,不会修改持久化的默认配置。它的实现原理是把目标菜单项写入grubenv中的next_entry变量,GRUB在启动时会优先读取next_entry,用完即清。这个命令在我处理"新内核可能有问题,先试一次旧内核"的场景里特别好用,相当于给系统做了一个单次启动的快照切换。

我个人在调试内核参数、做硬件兼容性验证时,最常用的其实是这个命令。因为不需要改配置文件,也不用担心改完忘了改回来,非常适合"只试一次"的场景。

四种方案的对比,我整理成一张表:

方案配置方式适用场景缺点
序号GRUB_DEFAULT="1>2"临时快速指定可读性差,内核升级后易偏移
名称GRUB_DEFAULT="Advanced options for Ubuntu>..."长期固定名称必须精确匹配
savedGRUB_DEFAULT=saved + grub-set-default频繁切换默认项会跟随手动选择变化
grub-reboot命令行指定单次临时试用仅影响下一次启动

4. 刷新配置与启动验证

4.1 update-grub到底做了什么

不管用哪种方案修改了/etc/default/grub,最后一步都需要刷新GRUB配置。在Ubuntu上就是执行:

sudo update-grub

这个命令是一个脚本封装,底层调用的是grub-mkconfig -o /boot/grub/grub.cfg。它会扫描/etc/grub.d/目录下的脚本(00_header、10_linux、30_os-prober等),结合/etc/default/grub里的参数,重新生成grub.cfg。简单理解就是:模板配置 + 检测逻辑 = 最终配置,也就是说你改完模板之后,必须让系统重新"翻译"一遍,grub.cfg才会带上你的新设置。

执行update-grub后,观察输出有没有出现你期望的内核名称。它通常会打印类似:

Found linux image: /boot/vmlinuz-5.15.0-92-generic Found initrd image: /boot/initrd.img-5.15.0-92-generic Found linux image: /boot/vmlinuz-5.15.0-91-generic Found initrd image: /boot/initrd.img-5.15.0-91-generic done

如果你改了GRUB_DEFAULT但看不到任何"found"输出,说明update-grub没有正确执行,多半是权限不够、磁盘空间不足或其他问题。另外注意update-grub需要root权限,别漏了sudo。

有一点要提醒:如果GRUB_DEFAULT指向的菜单项名称在当前系统里不存在,update-grub不会报错,它只是照常生成配置,重启时GRUB找不到对应菜单项,会静默回退到第一个可启动项。所以刷新完配置后,最好再看一眼grub.cfg内容,确认目标内核确实在菜单里。

4.2 重启后如何验证生效

完成配置后,重启系统:

sudo reboot

重启后进入系统,我建议第一件事就是确认当前到底用了哪个内核:

uname -r

如果你看到的就是目标内核版本,说明配置生效了。有时候你以为改对了,但uname -r出来还是老内核,这时候不要慌,我遇到过好多次,原因基本集中在几个点:

  • 没执行update-grub,grub.cfg还是旧的
  • 修改/etc/default/grub时语法写错了,比如引号没闭合、>前后多了空格
  • GRUB_DEFAULT的值指向了不存在的菜单项名称
  • UEFI机器上出现了多个GRUB引导项,BIOS实际引导的是另一个EFI分区里的GRUB

最后一种情况在双系统、多硬盘环境下特别常见。你改的可能是某个磁盘上的GRUB,但主板实际引导的是另一个EFI分区里的GRUB。排查方法很简单:开机时手动进入BIOS/UEFI设置界面,确认默认引导项到底是哪个,或者用efibootmgr命令查看当前引导顺序:

efibootmgr

这个命令会显示EFI引导项的列表,你可以看到当前BootOrder指向哪个设备。如果系统没有efibootmgr,可以用apt install efibootmgr安装。如果你发现引导顺序里排在最前面的不是Ubuntu的grubx64.efi,那修改/etc/default/grub大概率不会生效,得先把引导顺序调整到正确的EFI分区。

4.3 配置撤销或回滚

改完配置后如果发现不对劲,恢复原状也简单。编辑/etc/default/grub,把GRUB_DEFAULT改回0,然后:

sudo update-grub

如果你不确定改之前是什么值,Ubuntu默认就是GRUB_DEFAULT=0。还有一种更稳妥的方式:改配置文件之前先备份:

sudo cp /etc/default/grub /etc/default/grub.bak

出问题时恢复备份再update-grub即可。不要小看这一步,在生产机器上操作时,备份一下配置文件成本几乎为零,但真遇到配置写错、还要现场救急的时候,这一条能帮你省下不少时间。

5. 常见坑与排查经验

5.1 GRUB菜单序号偏移问题

用序号方式配置默认内核,最让人头疼的就是"内核升级后序号乱了"。比如你设置了GRUB_DEFAULT="1>2",本来指向前一个内核。结果某天apt自动升级内核,系统装了一个5.15.0-93,菜单排列变成了:

  • Ubuntu
  • Advanced options for Ubuntu
    • 5.15.0-93
    • 5.15.0-93 (recovery mode)
    • 5.15.0-92
    • 5.15.0-92 (recovery mode)
    • 5.15.0-91(你要锁定那个)

原来序号2对应的是5.15.0-91,现在变成了5.15.0-92,你的默认项"悄悄"变成了另一个新内核,而你自己可能过了很久才发现。所以我前面才反复强调:如果需要长期固定,用菜单项名称而不是序号。

判断自己是不是踩了序号偏移的坑,最简单的方法是执行update-grub后,重新检查一遍grub.cfg里的菜单顺序,和自己配置文件里的序号对照一下。这个习惯我基本已经养成了,每次升级内核后都会顺手看一眼。

5.2 内核升级后默认项被重置

这个问题在20.04之后的Ubuntu上其实已经有改进了,但偶尔还是会遇到。原因通常是/etc/default/grub里的GRUB_DEFAULT=saved配合GRUB_SAVEDEFAULT=true时,GRUB会默认把"最后一次手动选择"当作默认。如果你在某个时间点手滑在菜单里选了一次别的内核,之后默认项就会跟着变。

避免办法有两种:一是用固定的菜单项名称,别用saved;二是在确认默认项正确之后,重新执行一次grub-set-default把grubenv里的记录写死。我个人更推荐第一种,省心。

5.3 GRUB菜单不出现的处理

有同学改完配置,发现开机直接进系统,GRUB菜单一闪而过甚至完全看不到,想手动选内核都没机会。这是因为/etc/default/grub里有这么一行:

GRUB_TIMEOUT_STYLE=hidden GRUB_TIMEOUT=0

GRUB_TIMEOUT_STYLE=hidden会隐藏菜单,GRUB_TIMEOUT=0表示等待时间为0,系统会直接用默认项启动。如果你需要经常切内核,建议把GRUB_TIMEOUT改成3或5,并且把GRUB_TIMEOUT_STYLE设为menu。注意这两个变量必须配套修改,只改一个不会生效。

另外有一个救命技巧:即使GRUB菜单被隐藏,开机时按住Shift键(传统BIOS模式)或反复按Esc键(UEFI模式)也能把菜单呼出来。当你把默认项改错、系统都快启动不起来的时候,这个技巧能救急。

5.4 清理旧内核的正确姿势

修改默认内核之后,很多人会顺手想把旧内核删掉,腾出/boot空间。但删除内核一定要小心,别把当前正在使用的内核删了。我的建议是先确认当前内核:

uname -r

然后删除不需要的内核镜像:

sudo apt remove --purge linux-image-5.15.0-91-generic linux-headers-5.15.0-91-generic

如果想更省事,也可以:

sudo apt autoremove

autoremove会清理不再需要的旧内核包,但有的时候它也会把你还想保留的内核一并清掉,所以执行前先看一下它将移除的包列表,确认没有当前正在运行的版本再继续。删除完成后记得:

sudo update-grub

让GRUB菜单同步更新。这个顺序很重要,如果你先删内核再update-grub,没问题;但如果先update-grub再删内核,grub.cfg里就会残留指向已删除内核的菜单项,启动时GRUB会报错。

5.5 遇到grub rescue模式怎么办

聊到GRUB相关的坑,就不得不提让无数人头疼的grub rescue模式。屏幕上一行"grub minimal bash like line editing is supported",说明GRUB找不到正常的配置文件或内核镜像,进入了最小化救援环境。这个情况我见过太多次了,常见原因有两个:

一是删内核删出了事,比如不小心把包含当前内核的vmlinuz文件从/boot里删掉了,或者操作时断电导致grub.cfg损坏。

二是双系统安装或Ubuntu重装时,GRUB安装位置不对,导致引导记录指向了不存在的分区。

如果是第一种情况,你大概率需要从Ubuntu安装U盘启动,进入Live环境后chroot到系统里,重新安装GRUB。下面是核心步骤(前提是你能在Live环境中挂载系统分区):

sudo mount /dev/sda2 /mnt sudo mount /dev/sda1 /mnt/boot/efi sudo mount --bind /dev /mnt/dev sudo mount --bind /proc /mnt/proc sudo mount --bind /sys /mnt/sys sudo chroot /mnt grub-install /dev/sda update-grub exit sudo reboot

这里的/dev/sda2要替换成你的根分区,/dev/sda1是EFI分区(UEFI模式下必须有),/dev/sda是目标磁盘。如果你的/boot是独立分区,还需要在chroot前额外挂载sudo mount /dev/sdXX /mnt/boot。这个操作能重建GRUB引导,一般都能救回来。

如果是第二种情况,麻烦在于"哪个磁盘才是真正的启动盘"。我的排查思路是:先查看当前系统的分区布局,确认系统安装在哪个硬盘上,然后用grub-install把GRUB装到对应磁盘的EFI分区里,最后检查主板启动顺序。

注意:grub-install是风险较高的操作,在双系统和多硬盘环境里尤其要小心。如果你不确定某个分区是根分区还是EFI分区,先执行lsblk -f看清楚,不要在没确认的情况下乱挂载、乱安装。

5.6 多系统环境下GRUB的默认项

既然要聊双系统安装Ubuntu,我也顺带提一句。双系统环境下,GRUB菜单往往会多出Windows Boot Manager这一项(由30_os-prober脚本检测)。如果你想修改默认启动的系统,依然可以用GRUB_DEFAULT来指定,比如:

GRUB_DEFAULT="Windows Boot Manager"

但Windows Boot Manager在不同主板上的显示名称可能有差异,有的显示成"Windows Boot Manager (on /dev/sda2)",这里的名称同样要和grub.cfg里的menuentry完全一致。多系统环境下建议先用grep命令把真实菜单名称抓出来,再填到配置里。

另外,双系统环境下如果网络、硬件和某个内核版本有兼容问题,锁定Linux内核同样用第3节的方法,逻辑完全一致。

写到这里,修改GRUB默认启动内核的几种方案、底层原理和常见坑基本都覆盖到了。我个人在实际操作中的体会是:能靠菜单项名称锁定就尽量别用序号,能分清楚"永久修改"和"单次切换"这两类需求,就不会在GRUB配置上反复折腾。最后再分享一个小技巧:每次update-grub之后,随手执行一下grep menuentry /boot/grub/grub.cfg,扫一眼菜单顺序,几秒钟的功夫,能避免很多次重启后才发现选错内核的尴尬。这个事儿多操作两次就熟了,剩下的就是自己动手试一次。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询