ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Ubuntu内核安装与版本切换:GRUB、DKMS、Secure Boot

Ubuntu内核安装与版本切换:GRUB、DKMS、Secure Boot Ubuntu 内核安装及版本切换这件事我在过去几年里被问过不下几十次。原因其实很朴素Ubuntu 每次常规更新都会悄悄把内核往前推一个小版本多数时候你毫无感知直到某天新显卡驱动装不上、无线网卡搜不到信号、外接显示器开始闪屏、虚拟机模块加载报错你才反应过来——内核变了。而这个时候如果不会看当前跑的是哪个内核、不会装指定版本、不会从新内核退回旧内核基本就只能重装系统一整个下午就这么没了。这篇内容就是冲着这类问题来的。我会从内核包的命名规则讲起把 APT 仓库安装、主线 deb 包安装、源码编译安装三条路径完整走一遍再把 GRUB 版本切换、旧内核清理、Secure Boot 签名、DKMS 模块失效这些坑逐个填上。不管你是刚装完 Ubuntu 的新手还是天天和服务器、开发板打交道的老手这里都有能直接复制粘贴的命令和判断依据。前提只有一个你得进得去终端。剩下的交给我。1. 内核版本号背后的门道先把命名读懂再动手1.1 一个内核包名拆成四段来读新手最容易懵的地方就是dpkg -l | grep linux输出一大串包名看着都像但又都不一样。拿最典型的一个包名举例linux-image-5.15.0-91-generic。这四个部分各自有明确含义读懂了它你就能在几十个包里一眼挑出自己要的那个。linux-image是包类型。linux-image-*是内核映像本体linux-headers-*是编译外部模块用的头文件linux-modules-*是内核模块linux-modules-extra-*是那些没被打进主模块包的额外驱动很多无线网卡、部分存储控制器都在这里。5.15.0是上游主版本号对应 kernel.org 上的主线版本。-91是发行版构建号Ubuntu 自己在 5.15 基础上打补丁、修 CVE 后重新构建的次数。这个数字会随着安全更新不断变大而主版本号可能几个月都不动。-generic是flavour风味通用桌面和服务器就是generic云主机上常见virtual实时内核是lowlatency/realtime。flavour 不同编译进去的配置项完全不同不要混装。还有一个容易忽略的细节linux-image-unsigned-5.15.0-91-generic和linux-image-5.15.0-91-generic是两回事。前者是未签名版本后者是签过名的。在开启 Secure Boot 的机器上装错那个就直接起不来。这个坑后面第 6 节会详细说。1.2 两套版本概念千万别混我在带人的时候发现卡住最多的问题不是命令不会敲而是概念混了。内核这里其实有两套独立的版本号在跑第一套是上游版本也就是5.15、6.2、6.5这种它决定了内核本身有哪些新特性比如新的文件系统支持、新的调度器、新的驱动。你想用某个新硬件往往是被这套版本卡住的。第二套是发行版构建号也就是-91、-92这一串。Ubuntu 会在同一个上游版本上不断打补丁重建所以你从5.15.0-88升级到5.15.0-91上游版本一点没变但里面已经修了好几个安全漏洞。这两套号的更新节奏完全不同。构建号更新是自动的、频繁的基本每次apt upgrade都可能动上游版本更新则要看 Ubuntu 的发布策略——长期支持版LTS默认锁在某个上游版本想换大版本得手动装 HWEHardware Enablement内核或者去装主线内核。搞不清这一点就会出现我明明升级了怎么还是 5.15这种困惑。1.3 四种内核来源各自适合谁实际工作里能拿到的内核来源就四类我列个表对比一下你按自己的场景挑来源版本节奏稳定性签名情况适合场景发行版自带GA 内核跟 LTS 走很少换大版本最高官方签名服务器、生产环境、求稳用户HWE 内核跟着新的 LTS 升级高官方签名新硬件桌面用户、笔记本主线内核mainline紧跟 kernel.org中未签名尝鲜、验证新驱动、临时救急源码自编译完全自定义取决于你的配置需自行签名内核开发、加自定义补丁、裁剪体积GA 内核是 Ubuntu LTS 出厂自带的那套优点是无脑稳缺点是老硬件驱动全、新硬件驱动缺。HWE 是 Ubuntu 为桌面用户准备的新内核 老系统组合装完系统后如果你用的是比较新的笔记本很可能已经自动装了 HWE。主线内核是 Ubuntu 官方在 kernel.org 基础上做的构建好处是新坏处是少了 Ubuntu 的大量补丁各种 DKMS 模块尤其是显卡驱动经常翻车。源码编译就是完全掌控但维护成本最高。我个人的建议是先用 HWE不行再考虑主线最后才自己编译。绝大多数新硬件不识别的问题装 HWE 就解决了根本轮不到自编译。2. 开工前的准备三分钟摸清底细半小时省下重装2.1 一份命令清单先把当前状态摸透动手之前先把下面这几条跑一遍输出截图存好。这不只是习惯问题是你后面万一翻车时的病历本。uname -r # 当前运行的内核版本最关键的一条 uname -a # 完整信息含架构 lsb_release -a # 发行版代号22.04 是 jammy24.04 是 noble dpkg --print-architecture # amd64 还是 arm64别下错包 ls -lh /boot # 看 /boot 里现有哪些内核 df -h /boot # 看 /boot 还剩多少空间 mokutil --sb-state # Secure Boot 是否开启非常关键 dkms status # 有哪些外部模块需要跟着内核重编uname -r这一条是所有操作的基础你后面选哪个包、改哪个 GRUB 条目全都要对着它来。mokutil --sb-state这条很多教程都不提但它能直接决定你自编译内核能不能加载模块我在第 6 节会展开。2.2 /boot 空间是硬约束动手前必须确认Ubuntu 默认安装时给/boot分区通常只有 500MB 到 1GB而一个完整的内核image modules modules-extra headers加起来轻松超过 200MB。也就是说装两三个内核/boot 就满了。/boot满了之后的症状特别有迷惑性apt upgrade报No space left on device然后 dpkg 进入半配置状态接着apt装任何东西都失败甚至apt -f install也修不好。很多人这时候慌了去重启结果因为 initramfs 没生成成功直接进不了系统。所以在装新内核之前先跑df -h /boot看一眼。如果可用空间低于 400MB我建议先按第 5 节的方法清掉最老的那个内核腾出空间再装。清理的时候有个铁律永远保留至少两个能启动的内核。一个是你当前正在跑的另一个是备用的。只留一个万一新内核起不来你就只能靠 Live USB 救援了。2.3 回滚预案快照、备份与至少留一个能启动的内核虚拟机里折腾的朋友动手前直接打一个快照这是成本最低的保险。物理机如果用的是 LVM 或者 BtrfsUbuntu 自带的 Timeshift 也能做系统快照记得把/boot一起纳入备份范围。不做快照也行但至少要确认三件事一是 GRUB 菜单能正常呼出来开机按住 Shift 或反复按 Esc二是 GRUB 里能看到多个内核条目三是你知道怎么进 GRUB 命令行模式也就是在菜单上按c进入的那个界面。真到了黑屏起不来的时候能在 GRUB 里手动指定内核和根分区启动这一招能救回不少场子。备份层面/etc/default/grub和/etc/fstab这两个文件先拷一份到 U 盘或者云盘。它们的体积可以忽略不计但重装系统时能帮你省下大量还原时间。3. 安装新内核的三条路径与完整实操3.1 路径一APT 仓库安装首选最稳99% 的情况你应该走这条路。它装出来的是 Ubuntu 官方签名包Secure Boot 友好DKMS 兼容性最好。先更新索引并列出仓库里可用的内核包sudo apt update apt-cache search linux-image | grep generic apt-cache policy linux-image-genericapt-cache policy这条很值得看它会告诉你当前系统里装的版本、仓库里可用的版本以及这个元包依赖的具体内核版本。看明白之后有两种装法。装 HWE 内核推荐给新硬件桌面用户sudo apt install --install-recommends linux-generic-hwe-22.0422.04 对应linux-generic-hwe-22.0424.04 就是linux-generic-hwe-24.04。这个元包装完之后后续系统更新会把你的内核一直往新版本带。装指定版本的普通内核推荐给需要精确控制的场景sudo apt install linux-image-5.15.0-91-generic \ linux-headers-5.15.0-91-generic \ linux-modules-5.15.0-91-generic \ linux-modules-extra-5.15.0-91-generic注意我把四个包写全了。很多教程只写linux-image一个结果装完发现无线网卡没了、蓝牙没了——因为驱动在linux-modules-extra里。linux-headers也别省只要你机器上有任何 DKMS 模块NVIDIA 驱动、VirtualBox Guest Additions、部分网卡驱动没有头文件它们就编译不了。提示装完后apt会自动触发update-grub和update-initramfs。如果你在输出里看到这两步报错先别重启把错误看完再决定第 6 节有对应的排查方法。3.2 路径二主线内核 deb 包手动安装尝鲜 / 新硬件系统仓库里的内核版本跟不上的时候可以去 Ubuntu 官方维护的主线内核构建目录下载 deb 包。这个目录按上游版本分类每个版本下面有amd64、arm64等架构目录里面是四个文件linux-headers-版本_all.deb、linux-headers-版本-构建_amd64.deb、linux-image-unsigned-版本_amd64.deb、linux-modules-版本-构建_amd64.deb。下载齐之后一次性安装sudo dpkg -i linux-headers-*.deb linux-image-unsigned-*.deb linux-modules-*.deb sudo apt -f install # 补齐可能缺失的依赖这里有几个坑我必须提前说清楚。第一主线构建的 image 是unsigned未签名的Secure Boot 开着的机器装完重启会直接拒绝加载。第二主线内核没有 modules-extra 这个拆分所有模块都在linux-modules里看起来是好事但这些模块是用主线配置编译的某些依赖 Ubuntu 特定补丁的驱动会失效。第三DKMS 模块十有八九要重编而且失败率明显高于官方内核。我的经验是主线内核只用来临时验证某个新硬件能不能被识别验证完就该退回官方内核别当长期方案用。3.3 路径三源码编译安装含配置与并行度计算自编译是最后手段但有些场景确实绕不开比如你要打自定义补丁、要裁掉大量用不到的驱动来减小体积、要做 eBPF 或者内核模块开发。完整流程如下。先装编译依赖这一串缺一个都会在编译中途报错sudo apt install build-essential libncurses-dev bison flex \ libssl-dev libelf-dev bc dwarves zstd然后去 kernel.org 下载对应版本的 tarball 并解压进入源码目录。接下来最关键的一步是沿用当前系统的配置而不是从零开始cp /boot/config-$(uname -r) .config因为 Ubuntu 的配置里有两个签名相关的选项会直接导致编译失败必须先关掉scripts/config --disable SYSTEM_TRUSTED_KEYS scripts/config --disable SYSTEM_REVOCATION_KEYS make olddefconfigmake olddefconfig的作用是用你已经了解的配置为基础把新版本内核里新增的选项全部按默认值填上同时不交互提问。如果你需要精细裁剪这时候改用make menuconfig手动勾选。编译的并行度计算是个实际经验问题。常见的说法是make -j$(nproc)但每个 gcc 进程峰值可能吃掉 1.5GB 到 2GB 内存。所以我一般这样估并行任务数 min(CPU 核心数, 可用内存 GB 数 / 2)。比如 8 核 16GB 的机器-j8刚好如果只有 8GB 内存那就老实-j4否则会触发 OOM killer 把编译进程干掉报一堆莫名其妙的错误。make -j$(nproc) # 内存充足时直接用核心数 # 内存偏紧时改成具体数字例如 make -j4编译时间心里要有数8 核机器跑一个完整内核差不多 15 到 30 分钟老笔记本可能要一两个小时。编译完成后依次执行sudo make modules_install sudo make installmake install在 Ubuntu 上会自动把内核复制到/boot、运行update-initramfs并调用update-grub。跑完ls /boot能看到新版本的 vmlinuz 和 initrd.img就说明装好了。3.4 装完必做的验证动作不管走哪条路径装完都先做这几步验证再重启。这能让你在重启前就发现问题而不是在重启后对着黑屏发愁。grep -n menuentry\|submenu /boot/grub/grub.cfg # 确认新内核进了 GRUB 菜单 ls -lh /boot/vmlinuz-* /boot/initrd.img-* # 确认映像文件齐全 dkms status # 确认外部模块是否已为新内核编译 df -h /boot # 确认空间没被撑爆dkms status的输出会一行行列出模块名、版本和目标内核版本状态显示installed才算正常。如果显示build: incomplete说明头文件缺失或者编译失败这时候去重启进系统后大概率是没显卡驱动、分辨率锁在 1024x768 的状态。4. 版本切换让 GRUB 听你的话4.1 临时切换开机菜单里选一次最简单的切换方式就是在开机时进 GRUB 菜单选一次。默认情况下 Ubuntu 把菜单藏起来了你需要开机时按住 ShiftBIOS 机型或者反复按 EscUEFI 机型把它呼出来。菜单里会有一个Advanced options for Ubuntu进去之后就是各个内核版本选你想启动的那个即可。这种切换只影响本次启动下次重启还是会用默认项。适合的场景是新内核装好了但心里没底先手动试一次确认没问题再设成默认。顺带提一个更优雅的办法grub-reboot可以指定仅下次启动用哪个条目而且不用手动按住键盘sudo grub-reboot 12 # 数字含义下一小节讲 sudo reboot4.2 永久切换改 /etc/default/grub 的正确写法真正的版本切换是在/etc/default/grub里做。用编辑器打开这个文件关注这四个参数GRUB_DEFAULT0 GRUB_TIMEOUT_STYLEmenu GRUB_TIMEOUT10 GRUB_SAVEDEFAULTtrueGRUB_DEFAULT决定默认启动项取值方式有三种我按推荐程度排列。第一种是按标题指定最直观也最不容易数错GRUB_DEFAULTAdvanced options for UbuntuUbuntu, with Linux 5.15.0-91-generic这里前面的部分是一级菜单的标题后面是子菜单里的条目标题。标题必须和/boot/grub/grub.cfg里写的一字不差所以改之前先用grep menuentry /boot/grub/grub.cfg把准确标题抄下来。第二种是按序号指定格式是一级序号二级序号注意序号从 0 开始数。比如12表示一级菜单的第 2 项也就是 Advanced options里的第 3 个条目。这种写法在系统更新后会失效因为序号会变所以不太推荐用于长期配置。第三种是配合GRUB_DEFAULTsaved和GRUB_SAVEDEFAULTtrue这样每次你在菜单里选了哪个系统会记住它作为下次的默认项。适合那种需要频繁切换调试的场景。改完参数之后必须执行下面这条才会生效sudo update-grub很多人改完配置文件直接重启发现没变化就是因为漏了这一步。update-grub会根据/etc/default/grub重新生成/boot/grub/grub.cfg真正被 GRUB 读取的是后者。4.3 子菜单序号到底怎么数这一节我单独拎出来讲因为它坑过太多人。假设grub.cfg里长这样menuentry Ubuntu { ... } submenu Advanced options for Ubuntu { menuentry Ubuntu, with Linux 6.5.0-27-generic { ... } menuentry Ubuntu, with Linux 6.5.0-27-generic (recovery mode) { ... } menuentry Ubuntu, with Linux 5.15.0-91-generic { ... } menuentry Ubuntu, with Linux 5.15.0-91-generic (recovery mode) { ... } } menuentry Memory test { ... }那么GRUB_DEFAULT12的含义是一级菜单第 1 项是Advanced options for Ubuntu第 0 项是 Ubuntu二级菜单第 2 项是Ubuntu, with Linux 5.15.0-91-generic。注意 recovery mode 条目也占序号所以千万不要漏数。我自己的习惯是永远用标题写法宁可多花十秒去 grep 准确字符串也不去数数字。因为系统每次内核更新序号全乱而标题是稳定的。4.4 切换后别忘了重建 initramfs如果你是用 APT 或 dpkg 装的官方内核包initramfs 会自动生成。但如果你是自己编译或者手动拷贝文件到/boot的一定要手动重建sudo update-initramfs -u -k 5.15.0-91-generic # 指定版本重建 sudo update-initramfs -u -k all # 全部重建initramfs 是开机早期加载的那个临时根文件系统里面装着挂载真实根分区所需的驱动。它和内核版本是配套的版本对不上就会出现(initramfs)提示符或者Unable to find root device。这个错误看着吓人实际上十有八九就是 initramfs 没重建或者重建失败。5. 旧内核清理别让 /boot 撑爆5.1 安全清理的三步法内核装多了必须清但清理动作要谨慎因为 dpkg 对内核包的处理比较特殊。我一般按这三步走。第一步列出所有已安装的内核相关包找出你想清的那个完整版本号dpkg -l linux-image-* linux-headers-* linux-modules-* | grep ^ii第二步确认这个版本不是当前正在运行的uname -r # 输出如果等于你要删的版本立刻停手第三步把同一个版本号的四个包一起清掉不要只删 image 留下 headerssudo apt purge linux-image-5.15.0-88-generic \ linux-headers-5.15.0-88-generic \ linux-modules-5.15.0-88-generic \ linux-modules-extra-5.15.0-88-generic用purge而不是remove因为remove会留下配置文件时间长了/etc里会堆一堆没用的残留。清完之后跑一次sudo update-grub刷新菜单再df -h /boot确认空间释放出来了。5.2 autoremove 的陷阱sudo apt autoremove --purge看起来很省事它会自动清掉不再被依赖的旧内核。但这里有个隐患这个命令判断依赖关系的依据是元包如果你的系统里装了linux-generic这种元包autoremove 可能会把你手动装的、正在用来救急的那个特定版本也一并清掉。所以我的做法是先手动指定版本清掉最老的两三个剩下的再交给 autoremove 收尾。而且在执行 autoremove 之前一定看一眼它的预览列表确认里面没有你当前运行的内核和你想保留的备用内核。apt 通常会提示The following packages will be REMOVED那几行字必须读完。5.3 /boot 已经满了怎么救这是最让人绝望的场景/boot满了apt 卡在半配置状态装不了也删不掉因为 dpkg 需要先完成上一个操作。救援流程是这样的。先看 dpkg 处于什么状态sudo dpkg -l | grep -E ^i[^i]|^.[^i] | grep linux如果状态列显示iF半配置或者iU解包未配置说明有个操作没做完。这时候先腾空间最直接的办法是把/boot里最老的vmlinuz-*、initrd.img-*、config-*、System.map-*这一组文件临时挪到别的地方比如/root/boot-backup腾出几百 MB。注意挪文件只是临时手段挪完必须立刻完成 dpkg 的收尾否则 GRUB 菜单里会残留指向已删除文件的条目选到就报错。空间腾出来之后把 dpkg 的状态修好sudo dpkg --configure -a sudo apt -f install然后立刻用第 5.1 节的方法把旧内核正式清掉再把临时挪走的文件删掉。整个过程不要重启重启很可能进不去系统。6. 常见故障与排查速查6.1 装完进不去系统黑屏、卡 initramfs、紧急模式黑屏只有一个光标在闪最常见的原因是显卡的 KMS 驱动和新内核不匹配。临时救急的办法是在 GRUB 菜单里选中出问题的条目按e进入编辑模式在linux那一行的末尾加上nomodeset然后按 CtrlX 启动。能进系统之后立刻切回旧内核第 4 节的方法再慢慢排查新内核的问题。停在(initramfs)提示符通常是根分区找不到。先直接敲exit它会把详细的错误信息打出来重点看是不是Unable to find root device或者某个 UUID 找不到。如果确认是 initramfs 的问题用旧内核进系统后重建即可。进到紧急模式emergency mode一般是文件系统检查失败或者 fstab 挂载点有问题。这时候根分区是只读挂载的先mount -o remount,rw /才能改文件。多数情况下fsck一下根分区就能解决。6.2 DKMS 模块与显卡驱动失效换内核之后分辨率变得很低、外接显示器不亮、nvidia-smi报找不到设备基本都是 DKMS 模块没跟着新内核重编。排查顺序是这样的dkms status输出里如果有模块的版本行显示了目标内核但状态不是installed就手动触发一次重编sudo dkms autoinstall -k $(uname -r)如果报错说找不到内核头文件那就是你没装linux-headers-$(uname -r)装上再跑一次。NVIDIA 驱动的话最省事的办法是重装对应的 dkms 包sudo apt install --reinstall nvidia-dkms-535 # 版本号按你实际装的填还有一个高频坑主线内核的目录结构和官方内核略有差异DKMS 可能找不到构建目录。可以让它显式指定sudo dkms install -m nvidia -v 535.154.05 -k 6.5.0-27-generic6.3 Secure Boot 与自编译内核的签名问题这是我见过最隐蔽的一类问题。症状是模块明明编译成功了insmod却报Operation not permitted或者启动日志里出现Loading of unsigned module is tainted。先确认 Secure Boot 状态mokutil --sb-state如果输出SecureBoot enabled那么任何未签名的内核模块都会被拒绝加载。Ubuntu 官方内核带 Canonical 的签名所以装官方包没问题你自己编译的内核就是未签名的模块加载会全部失败。处理方式有两条路。一条是进 BIOS 关掉 Secure Boot简单粗暴适合开发机。另一条是自己给模块签名用scripts/sign-file配合自签密钥再通过mokutil --import把公钥注册进 MOKMachine Owner Key列表重启时在蓝色的 MOK 管理界面确认导入。自签流程值得单独写一篇这里只提醒你自编译内核之前先看一眼 Secure Boot 状态能省下大量无谓的调试时间。6.4 报错速查表现象大概率原因处理方式开机黑屏只有一个光标显卡 KMS 驱动不匹配GRUB 加nomodeset临时进入切回旧内核停在(initramfs)根分区或加密卷模块缺失敲exit看报错用旧内核重建 initramfs无线网卡、蓝牙消失缺linux-modules-extra补装对应版本的 modules-extra 包dkms status显示 incomplete缺内核头文件或编译失败装 headers再dkms autoinstallapt报No space left on device/boot满先临时腾空间再dpkg --configure -a修状态模块加载报Operation not permittedSecure Boot 拒绝未签名模块关闭 Secure Boot 或走 MOK 签名流程update-grub后菜单没变没保存配置或没执行 update-grub检查/etc/default/grub并重跑命令改完GRUB_DEFAULT启动项没换用了序号写法而序号已变改用标题写法系统更新后旧内核被自动删了autoremove 清掉了备胎装linux-generic元包并保留两个内核7. 我踩过的几个坑和现在的固定习惯写到这里核心技术点基本都覆盖了。最后分享几个我自己在实际操作中形成的习惯都是被坑出来的希望能帮你少走点弯路。第一个习惯是永远保留两个内核并且明确知道备胎是哪两个。我会在装完新内核后立刻记一笔当前跑的是哪个备用的哪个。因为真出问题的时候人在黑屏面前是慌的根本想不起来上一个能用的版本号是什么。写在便签上贴在显示器边上听起来很土但极其有效。第二个习惯是估算 /boot 占用而不是凭感觉。我现在装新内核之前一定先跑df -h /boot低于 400MB 就先清理。这个阈值不是拍脑袋定的一个内核组image modules modules-extra headers initrd实际占用在 200MB 上下加上 GPG 签名和旧 initramfs 备份400MB 是比较安全的启动线。第三个习惯是主线内核只用不养。我只有在需要验证某个新硬件的时候才装主线内核验证完立刻回退从不把它设成 GRUB 默认项。因为主线内核没有 Ubuntu 的补丁NVIDIA 驱动、VirtualBox 增强功能、部分加密卷这几个东西的兼容性一直不稳养着它等于给自己埋雷。第四个习惯是改动之前先 grep 一遍 grub.cfg。不管用标题写法还是序号写法我都会先跑grep -n menuentry\|submenu /boot/grub/grub.cfg把真实的标题和层级抄下来再改配置。这一步花不了十秒但能避免改完没生效、反复重启折腾半小时的经典惨案。这套流程我大概在几十台机器上跑过从老旧的 ThinkPad 到机架服务器从虚拟机到开发板。真正会把你搞崩溃的从来不是编译内核本身而是 /boot 满了、initramfs 没重建、Secure Boot 拦了模块这一类外围问题。把这些提前想清楚安装和切换其实就是几条命令的事。
返回列表