Ubuntu 20.04 内核编译实战:从5.4.0源码到自定义启动 1. 项目概述与核心价值最近在折腾一个老项目需要在一个特定的Ubuntu 20.04 LTS环境里把内核从默认的5.4版本升级到5.4.0。你可能会问这听起来不就是同一个大版本吗有啥好折腾的这里面的门道可不少。Ubuntu 20.04默认搭载的内核版本号类似5.4.0-xx-generic后面的-xx是Ubuntu自己打的补丁序列号。而我需要的是那个最“纯净”的、上游社区发布的5.4.0主线稳定版内核。这通常是为了复现某个特定内核的Bug测试某个刚被主线合并的驱动或者像我一样满足某个老旧但关键的应用软件对内核版本的苛刻要求。自己动手编译安装一个指定版本的内核是Linux进阶路上绕不开的实操环节。这个过程不仅能让你彻底搞懂内核的构成更能让你对系统启动流程、模块管理、硬件抽象层有更深的理解。网上教程很多但要么过于简略跳过了关键细节要么一股脑扔给你一堆命令让人看得云里雾里。今天我就以Ubuntu 20.04为舞台带你走一遍从下载源码到成功启动新内核的完整流程我会把每个步骤背后的意图、可能遇到的坑以及我踩过之后总结的避坑技巧都掰开揉碎了讲清楚。无论你是运维工程师、嵌入式开发者还是单纯想深入Linux内核的爱好者这篇手记都能给你一份可靠的“地图”。2. 前期准备与环境检查在开始编译这个“大工程”之前充分的准备工作能避免你做到一半发现缺这少那前功尽弃。这个阶段的核心是搭建一个完整的编译环境并确认你的系统状态适合进行内核替换。2.1 系统更新与依赖安装首先确保你的系统是最新的。这不是为了追新而是为了获得最新的基础库和开发工具链避免因工具版本过低导致编译失败。sudo apt update sudo apt upgrade -y接下来安装编译内核所必需的工具和开发库。这个命令列表比较长但每一项都有其作用build-essential,libncurses-dev,bison,flex,libssl-dev这是内核编译的“核心四大件”分别提供GCC编译器集合、制作菜单配置界面的库、语法分析器生成工具和词法分析器生成工具以及SSL加密库的头文件。libelf-dev处理ELF可执行与可链接格式文件所必需现代内核构建离不开它。pkg-config帮助编译脚本自动查找库文件。git虽然我们这次是下载稳定版源码包但安装git是个好习惯方便后续获取补丁。kernel-package可选一个自动化制作deb安装包的工具在Ubuntu早期版本常用但现在更推荐直接使用make命令安装。为了过程清晰本文不依赖它。把这些包一次性安装上sudo apt install -y build-essential libncurses-dev bison flex libssl-dev libelf-dev pkg-config git注意在虚拟机或磁盘空间紧张的环境中需要留意/usr/src目录的空间。编译内核会产生大量中间文件整个过程可能需要15-20GB的剩余空间。使用df -h /usr/src命令检查一下。2.2 获取内核源码Linux内核源码官方仓库是 kernel.org 我们可以在这里找到所有稳定版内核的压缩包。我们需要的是5.4.0版本。进入/usr/src目录这是存放内核源码的传统位置方便管理。cd /usr/src使用wget下载源码压缩包。这里我选择.xz格式的压缩包它比.gz格式压缩率更高下载更快。sudo wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.4.tar.xz下载完成后解压源码包。tar命令的x代表解压J代表处理.xz格式f后面指定文件名。sudo tar -xJf linux-5.4.tar.xz解压后会生成一个linux-5.4的目录进入它这就是我们接下来工作的主目录。cd linux-5.4实操心得我习惯在下载后使用sha256sum或gpg验证源码包的完整性尤其是从非官方镜像下载时。kernel.org提供了对应的校验文件如linux-5.4.tar.sign但对于学习环境这一步可以省略。不过保持验证的习惯在生产环境中至关重要。2.3 清理与配置准备在编译之前我们需要一个配置文件.config。最稳妥的方法是基于当前运行内核的配置来生成新内核的配置基础这样可以最大程度保证新内核能驱动你现有的硬件。将当前系统的内核配置复制到源码目录。你的/boot目录下应该有一个config-$(uname -r)的文件。cp /boot/config-$(uname -r) .config接下来运行make olddefconfig命令。这个命令非常关键它做两件事读取我们刚刚复制的旧配置文件.config。静默处理新内核版本新增的配置选项。对于旧配置中已有的选项保持原样对于新增的选项使用其默认值defconfig自动填充而不会在终端弹出无数的交互式提问。make olddefconfig这个步骤能确保新内核的配置是有效的并且与你的系统环境兼容避免了手动配置的繁琐和出错风险。3. 内核配置详解与定制拿到基础的.config文件后我们进入了内核编译中最具灵活性也最容易让人困惑的环节——内核配置。Linux内核是高度模块化的你可以选择将哪些功能编译进内核y编译为可加载模块m或者直接排除n。合理的配置能在功能、性能和内核大小之间取得最佳平衡。3.1 使用交互式菜单配置虽然olddefconfig提供了一个可用的起点但我们通常需要根据实际需求进行调整。最直观的方式是使用基于ncurses的文本图形界面。make menuconfig执行这个命令后会打开一个蓝底白字的菜单界面。你可以使用方向键导航回车键进入子菜单或选择选项空格键在 二选一或[ ]三选一*编译进内核M编译为模块空为不编译之间切换状态。对于这次安装5.4.0内核我建议重点关注以下几个区域它们直接影响安装后的使用体验General setup - Local version - append to kernel release: 这是必须修改的一项。在这里你可以输入一个自定义字符串例如-mycustom它会被附加到内核版本号后面变成5.4.0-mycustom。这样做有两个巨大好处一是让你在uname -r的输出中清晰区分这是你自定义编译的内核而不是官方包二是防止你自定义的内核覆盖系统原有的内核模块目录/lib/modules/5.4.0-xx-generic避免模块管理混乱。Processor type and features: 在这里可以优化内核针对你CPU架构的微调。如果你的机器是物理机可以进入Processor family子项选择与你CPU匹配的型号如Core 2/newer Xeon这能启用一些特定的优化。对于虚拟机如VMware、VirtualBox保持默认的Generic-x86-64通常就是最好的选择。Device Drivers: 这是最庞大的部分。除非你非常确定你的系统需要某个极其特殊的硬件驱动否则强烈建议新手不要在这里做大量改动。基于现有配置olddefconfig已经包含了当前系统所需的绝大多数驱动模块。胡乱禁用可能导致硬盘、网卡等关键设备无法识别。File systems: 确保你使用的文件系统支持被编译至少是模块形式。对于Ubuntuext4是根文件系统的默认选择所以ext4驱动必须存在通常是*或M。如果你使用了btrfs、xfs或zfs也要在此确认相关支持已启用。踩坑记录曾经有一次我为了追求极简在File systems里把DOS/FAT/NT Filesystems支持关掉了结果重启后无法识别U盘和Windows分区给文件交换带来了很大麻烦。所以除非是嵌入式设备有严格尺寸限制否则对于桌面/服务器系统保持默认或编译为模块M是更安全的选择。配置完成后选择 Save 直接回车使用默认的.config文件名保存然后选择 Exit 直到退出菜单。3.2 非交互式配置调整如果你只是想修改一两个明确的配置项而不想进入整个菜单可以使用sed命令直接修改.config文件。例如强制开启某个选项为模块./scripts/config --file .config --set-str CONFIG_LOCALVERSION -mycustom或者启用某个之前被禁用的功能./scripts/config --file .config --enable CONFIG_BPF_SYSCALL使用scripts/config脚本比直接编辑.config文件更安全因为它能处理配置项的依赖关系。修改完成后建议再次运行make olddefconfig来理顺依赖并处理新增选项。4. 编译与安装全流程配置妥当接下来就是最耗时但也最“有成就感”的编译阶段。这个过程会大量消耗CPU和内存资源。4.1 启动编译过程使用make命令开始编译。为了充分利用多核CPU的性能强烈建议使用-j参数指定并行编译的任务数。一个常见的经验法则是设置为核心数的1到2倍。你可以用nproc命令查看逻辑核心数。make -j$(nproc)按下回车后屏幕上会开始飞速滚动编译信息。这个过程视机器性能而定在双核虚拟机里可能需要1个多小时而在八核的台式机上可能只需要20分钟。期间CPU使用率会接近100%这是正常现象。编译过程主要分几个阶段编译内核镜像vmlinux和核心模块这是主体。编译设备树如果适用主要用于ARM等嵌入式平台。生成压缩的内核镜像如bzImagex86架构。注意事项编译过程中如果报错最常见的两个原因是1. 依赖包没装全回头检查2.1节2. 配置存在冲突尝试make mrproper彻底清理后重新从cp /boot/config-xxx .config开始。错误信息通常会明确指出缺失的头文件或工具根据提示安装即可。4.2 安装模块与内核编译成功后我们需要将生成的内核模块和内核镜像文件安装到系统合适的位置。首先安装模块。这个命令会将编译好的所有内核模块.ko文件安装到/lib/modules/5.4.0-mycustom/目录下路径中的版本号包含你设置的LOCALVERSION。sudo make modules_install这个步骤很快。完成后你可以用ls /lib/modules/查看应该能看到一个新目录5.4.0-mycustom。接下来安装内核镜像。这个命令会将内核镜像bzImage或vmlinuz和符号映射文件System.map复制到/boot目录下并生成一个初始内存盘镜像initrd或initramfs。sudo make installmake install脚本主要做了以下几件事将arch/x86/boot/bzImage复制为/boot/vmlinuz-5.4.0-mycustom。将System.map复制为/boot/System.map-5.4.0-mycustom。调用update-initramfs工具为这个新内核生成对应的initrd.img-5.4.0-mycustom文件。这个initramfs非常重要它包含了在内核启动早期、根文件系统挂载前所必需的核心驱动模块比如你的磁盘控制器驱动、文件系统驱动没有它内核可能无法找到并挂载你的根分区。最后它会自动更新GRUB引导菜单。4.3 更新引导加载器GRUB虽然make install通常会自动更新GRUB但为了确保万无一失我们手动执行一下更新命令sudo update-grub这个命令会扫描/boot目录下所有可用的内核并重新生成GRUB的配置文件/boot/grub/grub.cfg。在输出信息中你应该能看到类似Found linux image: /boot/vmlinuz-5.4.0-mycustom的字样这表明你的新内核已经被GRUB识别。5. 重启验证与故障排查所有安装步骤完成后就是最激动人心也最需要谨慎的时刻——重启并进入新内核。5.1 重启与内核选择sudo reboot在重启过程中当出现GRUB引导菜单时Ubuntu默认可能隐藏需要快速按下Shift键或Esc键唤出你会看到一个新增的启动项通常名为“Ubuntu with Linux 5.4.0-mycustom”。使用方向键选择它然后回车启动。系统会开始加载新内核。观察启动过程中的信息如果没有出现致命的错误panic并最终成功进入登录界面那么恭喜你已经成功了90%。登录系统后打开终端使用以下命令验证uname -r如果输出是5.4.0-mycustom那么说明你正运行在自己编译的新内核上再用lsmod看看模块是否正常或者试试网络、声音等基本功能是否完好。5.2 常见问题与解决方案实录即使步骤再详细实操中也难免会遇到问题。下面是我和同事们总结的几个典型问题及排查思路。问题一重启后卡在黑屏或者提示“Failed to start Load Kernel Modules”可能原因内核模块安装不完整或者initramfs镜像生成失败、未包含必要的驱动尤其是磁盘或文件系统驱动。排查步骤重启进入GRUB选择之前旧的、正常的内核启动。进入系统后检查/lib/modules/5.4.0-mycustom/目录是否存在且内容是否丰富。如果目录为空或很小说明sudo make modules_install可能失败了。检查/boot目录下是否存在initrd.img-5.4.0-mycustom文件。如果不存在手动生成cd /usr/src/linux-5.4 sudo update-initramfs -c -k 5.4.0-mycustom再次更新GRUB并重启尝试。问题二新内核启动后某个硬件如无线网卡、特殊显卡无法工作可能原因该硬件的驱动在新内核配置中被禁用n或编译为模块M但未自动加载。排查步骤使用lspci -k或lsusb查看硬件信息确认内核为其加载了哪个驱动Kernel driver in use:一行。在旧内核中使用modinfo 驱动模块名查看该模块依赖的内核配置项parm:和depends:。回到内核源码目录使用make menuconfig根据配置项名称搜索按/键确保在新内核中该驱动被启用*或M。如果是模块M但未加载检查/etc/modules文件或创建相应的/etc/modules-load.d/配置文件来自动加载它。问题三编译过程中报错“recipe for target ‘…’ failed”可能原因这是最泛的编译错误。首先看错误信息的前几行。排查思路缺少头文件错误信息常包含fatal error: xxx.h: No such file or directory。你需要根据头文件名称安装对应的-dev包。例如缺少openssl/xxx.h就安装libssl-dev。语法错误如果是代码层面的语法错误极有可能是你的源码包损坏或者你的GCC版本与内核不兼容Ubuntu 20.04的GCC是9.3编译5.4内核完全没问题。重新下载源码包并验证完整性。内存不足在资源有限的虚拟机中可能会因内存不足OOM导致编译进程被杀死。尝试减少并行编译任务数make -j2或直接make。问题四GRUB菜单中没有出现新内核选项可能原因make install脚本执行有误或update-grub未正确识别。排查步骤确认/boot目录下确实有vmlinuz-5.4.0-mycustom、initrd.img-5.4.0-mycustom和System.map-5.4.0-mycustom这三个文件。检查/boot/grub/grub.cfg文件搜索“5.4.0-mycustom”看是否存在对应的menuentry段落。如果文件存在但GRUB没识别可以尝试彻底重建GRUB谨慎操作最好先备份sudo grub-install /dev/sda # 注意这里的sda是你的磁盘设备不是分区 sudo update-grub5.3 内核管理与回滚成功启动新内核后为了系统稳定和节省磁盘空间建议进行一些管理。删除旧内核模块如果你确定不再需要旧内核可以删除其模块目录。但务必保留至少一个能正常启动的旧内核作为备份sudo rm -rf /lib/modules/5.4.0-xx-generic # 将xx替换为你的旧内核版本号清理源码目录编译完成后源码目录下的*.o文件等中间产物可以清理以释放空间约10GB。cd /usr/src/linux-5.4 make clean # 清理大多数中间文件保留.config # 或者更彻底的清理会删除.config慎用 # make mrproper设置默认启动项如果你希望默认启动新内核可以修改GRUB配置。编辑/etc/default/grub文件找到GRUB_DEFAULT一行。你可以将其设置为新内核在GRUB菜单中的序号从0开始计数或者更稳妥地设置为菜单项的名称GRUB_DEFAULTAdvanced options for UbuntuUbuntu, with Linux 5.4.0-mycustom修改后运行sudo update-grub生效。最重要的回滚方案如果新内核完全无法启动你需要在GRUB启动时选择之前的旧内核进入系统。进入系统后你可以删除有问题的内核文件sudo rm /boot/vmlinuz-5.4.0-mycustom /boot/initrd.img-5.4.0-mycustom /boot/System.map-5.4.0-mycustom sudo rm -rf /lib/modules/5.4.0-mycustom sudo update-grub这样GRUB菜单中这个坏掉的选项就会被清理掉。整个流程走下来从准备到验证成功虽然步骤繁多但每一步都有其明确的目的。自己编译内核就像是为你的系统量身定制一件“内核盔甲”你能清楚地知道里面包含了什么去掉了什么。这种掌控感正是Linux的魅力之一。下次当你需要启用一个实验性功能或者为特定硬件打上一个尚未被发行版收录的补丁时这套流程就是你最有力的工具。记住保持耐心仔细阅读终端输出的每一条信息尤其是错误提示它们是你解决问题的最佳向导。