1. 项目概述:为什么要自己编译内核?

如果你已经用了一段时间的Linux,对命令行不再陌生,甚至开始琢磨怎么让系统跑得更快、更稳,或者想让它支持一块特殊的硬件,那么“编译内核”这个词迟早会进入你的视野。很多人把它看作Linux高手的“成人礼”,觉得过程复杂又容易出错。但说实话,一旦你亲手走完一遍,就会发现它更像是一个结构清晰的“高级定制”流程,远没有想象中那么可怕。内核是操作系统的绝对核心,它管理着CPU、内存、磁盘、网络等所有硬件资源,并为上层应用提供运行环境。主流发行版(如Ubuntu、Fedora)提供的内核是“通用配方”,为了兼容尽可能多的硬件,它包含了大量你可能永远用不到的驱动和功能模块。自己编译内核,本质上就是根据你的“硬件清单”和“功能需求”,重新做一份“专属菜单”,去掉冗余,强化核心,从而实现性能提升、功耗降低或特殊硬件支持。

我最早接触内核编译是为了在一台老旧的笔记本上跑一个极简的桌面环境,官方内核的图形栈开销太大。通过裁剪掉不需要的显卡驱动、声音支持和各种外设模块,不仅启动速度变快了,内存占用也少了将近200MB。后来在服务器上,为了优化网络吞吐量和虚拟化性能,也多次进行过内核定制。这个过程让我深刻理解了Linux的模块化设计哲学。所以,这篇指南不会只给你一串命令,我会结合这些年的实操经验,把每个步骤背后的“为什么”讲清楚,并分享那些容易踩坑的细节。无论你是想深入学习系统原理的开发者,还是追求极致性能的极客,或是需要为特定硬件(比如一块新的开发板)定制内核的嵌入式工程师,这篇从源码到启动的全流程拆解,都能给你一份可靠的“路线图”。

2. 编译前的核心准备:环境、源码与心态

动手之前,充分的准备能避免大半的麻烦。编译内核不是运行一个脚本那么简单,它对你的系统环境、资源状况甚至时间规划都有要求。

2.1 硬件与系统环境要求

首先,确保你有一个 稳定的运行环境 。这听起来像是废话,但至关重要。内核编译过程CPU和磁盘IO压力极大,持续时间长(从几十分钟到数小时不等)。

  • 物理机还是虚拟机? 对于初次尝试,我强烈建议在 虚拟机 (如VirtualBox、VMware)中进行。这提供了完美的“沙箱”,万一编译出的内核无法启动,你可以轻松回滚到快照,而不会影响宿主机。虚拟机分配的资源不用太高,4核CPU、8GB内存、50GB磁盘空间是一个比较舒适的起点。
  • 如果是物理机 ,请确保连接了 可靠的电源 (笔记本请插上电源适配器),并保证良好的散热。编译过程中CPU会持续高负载,过热可能导致降频甚至意外关机,前功尽弃。
  • 磁盘空间 :内核源码解压后大约1GB左右,但编译过程中会产生大量的中间文件和最终模块,建议预留 15-20GB 的可用空间。我曾经在空间不足的根目录下编译,到 make modules_install 时失败,清理起来非常头疼。
  • 内存 :至少需要4GB,8GB或以上更为稳妥。内存不足时,编译进程可能会被系统终止(OOM Killer)。

你的基础系统可以是任何主流的Linux发行版,如Ubuntu、Debian、Fedora、Arch等。本指南的命令以Debian/Ubuntu系(使用 apt 包管理器)为例,但原理相通,其他发行版只需替换对应的包管理命令即可。

2.2 获取内核源码:版本选择与安全下载

内核源码的官方仓库是 kernel.org 。网站首页清晰地列出了几种版本分支:

  • Mainline(主线) :由Linus Torvalds维护的最新开发版,包含所有新特性,但可能不稳定。 除非你是内核开发者或追求最新功能的测试者,否则不要用于生产环境。
  • Stable(稳定版) :从主线分支分离出来,经过测试和错误修复的版本。这是大多数桌面和服务器发行版使用的版本, 推荐大多数用户选择 。版本号如 6.8.12 。
  • Longterm(长期支持版,LTS) :某些稳定版会被标记为LTS,在数年(通常是2-6年)内获得长期的安全和维护更新。这是 服务器、嵌入式设备或追求极致稳定性的用户 的首选。例如, 6.6 系列就是一个LTS版本。

注意 :每个LTS版本都有明确的“生命周期终止(EoL)”日期。在EoL之后,将不再接收任何安全更新。为生产系统选择内核时,务必核对kernel.org上的EoL时间表。

下载方式 :

  1. 通过浏览器或wget :在kernel.org上找到想要的稳定版或LTS版tarball(通常是 .tar.xz 压缩格式),直接下载。
    wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.8.12.tar.xz
    
  2. 通过git :如果你想追踪主线开发或基于某个特定提交编译,可以使用git克隆。这便于后续更新和打补丁。
    git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
    # 或者克隆稳定版树
    git clone https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
    

安全警告 :务必从 kernel.org 或其官方镜像站下载。内核运行在最高权限级别(Ring 0),恶意修改的内核源码可能植入无法被常规安全软件检测的根级别后门,危害极大。

2.3 安装必要的编译工具链

编译内核需要一整套开发工具。不同发行版的包名略有差异,以下是基于Debian/Ubuntu的安装命令:

sudo apt update
sudo apt install git build-essential libncurses-dev flex bison libssl-dev libelf-dev bc dwarves zstd rsync

逐项解释一下这些包的作用,知其所以然才能应对可能出现的错误:

  • build-essential :包含了最关键的 gcc (GNU编译器集合)、 make (构建自动化工具)、 libc-dev (C标准库) 等基础编译工具。
  • libncurses-dev :提供 make menuconfig 命令所需的文本图形界面库,没有它就无法进行可视化的内核配置。
  • flex 和 bison :语法分析器生成器。内核配置系统(Kconfig)和某些子系统(如文件系统)的代码生成需要它们。
  • libssl-dev 和 libelf-dev :用于内核模块签名(安全特性)和ELF(可执行与可链接格式)文件处理。
  • bc :一种任意精度计算器语言,内核构建脚本在某些配置计算中会用到它。
  • dwarves :包含 pahole 工具,用于处理调试信息,对生成较新的内核(特别是启用 CONFIG_DEBUG_INFO 时)是必须的。
  • git , rsync :用于获取源码或应用补丁。
  • zstd :一种高效的压缩算法,现代内核源码包和编译输出可能用到它。

安装完成后,可以通过 gcc --version 和 make --version 来验证基础工具是否就位。

3. 内核配置详解:从拷贝到精调

这是整个流程中最核心、最考验耐心和理解的一步。配置决定了你的内核包含哪些功能、驱动和优化选项。

3.1 解压源码与初始配置准备

假设你下载的源码包是 linux-6.8.12.tar.xz 。

# 解压源码包
tar -xvf linux-6.8.12.tar.xz
# 进入源码目录
cd linux-6.8.12

进入目录后,第一件事是获取一个可用的初始配置。最简单可靠的方法,是 复用你当前运行内核的配置 。这能最大程度保证新内核能驱动你现有的所有硬件。

# 将当前系统的内核配置复制到源码根目录,并命名为 .config
cp /boot/config-$(uname -r) .config

$(uname -r) 会自动获取你当前内核的版本号。这个 .config 文件是一个包含成千上万个 CONFIG_*=y/n/m 键值对的文本文件, y 表示编译进内核主体, m 表示编译为可加载模块, n 表示不编译。

3.2 使用 menuconfig 进行可视化配置

现在,我们可以基于这个 .config 进行调整。运行:

make menuconfig

一个基于ncurses的蓝色文本界面会出现。这是与内核配置系统交互的主要方式。

界面导航指南 :

  • 使用 上下箭头键 移动高亮选择条。
  • 使用 左右箭头键 切换底部菜单( <Select> , <Exit> , <Help> )。
  • 回车键 进入子菜单或选中一个配置项。
  • 在配置项上按 空格键 循环切换其状态: [*] (编译进内核), [M] (编译为模块), [ ] (不编译)。有些选项只有 [*] 和 [ ] 。
  • 按 / 键可以搜索配置项,非常实用。
  • 连续按两次 <Exit> 或按 Esc 键两次可以退出当前菜单,并提示是否保存。

3.3 关键配置项解析与裁剪策略

面对海量选项,新手容易茫然。我的策略是: 先保证启动,再追求精简 。首次编译,建议只做少量安全修改。

1. 本地版本号(Local version) : 为了让你的自定义内核与官方包管理器的内核区分开,建议添加一个本地版本标识。 在 menuconfig 主界面,进入:

General setup --->
  (-mycustomkernel) Local version - append to kernel release

在括号内输入你的标识,如 -mycustomkernel 。这样编译出的内核版本号会是 6.8.12-mycustomkernel 。

2. 核心驱动与文件系统 :

  • 处理器类型与特性(Processor type and features) :这里可以针对你的CPU进行优化。例如,如果你的CPU是Intel的,可以找到 Processor family ,选择具体的型号(如 Core 2/newer Xeon ),内核会使用针对该CPU微架构的优化。 启用 CONFIG_MICROCODE 以支持CPU微码更新,这对修复CPU漏洞很重要。
  • 设备驱动(Device Drivers) :这是可以大幅裁剪的区域。例如,如果你使用的是NVMe固态硬盘,可以放心地去掉大量旧的IDE、SCSI控制器驱动。但 务必保留你当前硬件所需的驱动 。不确定时,在现有 .config 中搜索相关关键词(如 nvme , i915 for Intel GPU),看它们是如何设置的( =y 还是 =m ),依葫芦画瓢。
  • 文件系统(File Systems) :只保留你用的。例如,根分区是 ext4 ,就确保 ext4 是 [*] 或 [M] 。可以去掉 Btrfs , XFS , F2FS (如果你不用的话),以及 CD-ROM/DVD 文件系统( iso9660 , udf )等。

3. 内核调试与性能开销 : 对于生产或追求性能的系统,可以关闭调试信息以减少内核大小和轻微的性能开销。

Kernel hacking --->
  [ ] Kernel debugging
  Compile-time checks and compiler options --->
    [ ] Compile the kernel with debug info

关闭 CONFIG_DEBUG_INFO 可以显著减少内核镜像文件的大小。

4. 一个必须处理的关键问题(常见坑点) : 如果你复用了发行版的内核配置,在编译时可能会遇到与 内核模块签名 相关的错误,提示需要某些密钥。发行版为了安全,通常会启用模块签名,并使用它们自己的密钥。我们个人编译时没有这些密钥。 解决方法是在 .config 文件中 注释掉或删除以下三行 :

# 使用文本编辑器(如nano或vim)打开 .config
# 找到并确保以下行被注释(前面加#)或设置为空值
# CONFIG_SYSTEM_TRUSTED_KEYS=""
# CONFIG_MODULE_SIG_KEY=""
# 另外,对于某些较新GCC版本,可能需要关闭一个调试选项以避免section mismatch警告
# CONFIG_DEBUG_SECTION_MISMATCH=n

更稳妥的方法是在 menuconfig 中修改:

  • 进入 Cryptographic API ---> ,找到 Certificates for signature checking 相关选项,清空其中的密钥文件路径。
  • 进入 Enable loadable module support ---> ,在 Module signature verification 子选项中,将 CONFIG_MODULE_SIG 设置为 n (完全禁用模块签名)。对于学习和个人使用,禁用是安全的。

配置完成后,保存退出。系统会生成一个新的 .config 文件,并自动解决一些依赖关系(比如选中某个功能,其依赖项会被自动选中)。

4. 编译与安装:命令背后的原理与优化

配置完成后,就进入了最耗时的编译阶段。理解命令参数,能帮你更好地利用系统资源。

4.1 编译内核镜像(bzImage)与模块

编译的核心命令是 make 。但直接运行 make 会使用单线程,速度极慢。我们需要告诉 make 使用并行编译。

确定并行任务数(-j 参数) : 一个常见的经验公式是: CPU核心数 * 2 + 1 。例如,你的CPU有8个物理核心(可通过 nproc 命令查看):

make -j 17

这个公式源于希望保持CPU流水线始终饱和。 +1 是为了在某个编译任务因I/O(如磁盘读取)而阻塞时,仍有其他任务可以执行,最大化CPU利用率。如果你的内存较小(比如小于8GB),使用过高的 -j 值可能导致内存耗尽,编译进程被杀死。此时可以保守一点,使用 -j $(nproc) (等于核心数)或 -j $(( $(nproc) + 1 )) 。

编译过程分解 : 实际上, make 会根据 Makefile 的规则,依次完成以下主要目标(你可以分步执行,但通常一次性完成):

  1. make -j 17 :这默认会编译出最终的内核镜像文件 arch/x86/boot/bzImage (对于x86架构)以及所有标记为 =y 的内置组件。
  2. make -j 17 modules :编译所有标记为 =m 的可加载内核模块。

你可以将两步合并,直接运行 make -j 17 ,它会自动处理所有依赖。编译时间从几十分钟到数小时不等,取决于CPU性能、内核配置的复杂度和并发任务数。

编译过程中的注意事项 :

  • 保持系统专注 :编译期间尽量避免运行其他重型应用(如浏览器、虚拟机),以免争夺资源,延长编译时间或导致编译失败。
  • 留意警告和错误 :终端会持续输出信息。黄色的警告(Warning)通常可以忽略,但红色的错误(Error)会导致编译停止。最常见的错误是依赖缺失(回头检查工具链安装)或配置冲突(仔细阅读错误信息,通常与 .config 有关)。
  • 关于密钥的提示 :如果你没有完全禁用模块签名,在编译过程中可能会提示你生成密钥或输入密码。此时,直接按回车选择默认选项(生成临时密钥)或取消即可。

4.2 安装内核模块与引导文件

编译成功后,接下来是将成果安装到系统中。

第一步:安装内核模块

sudo make modules_install

这个命令会将编译好的所有模块( .ko 文件)复制到 /lib/modules/$(uname -r)-mycustomkernel/ 目录下(目录名是你的新内核版本号)。 sudo 权限是必需的,因为目标目录是系统目录。

第二步:安装内核镜像并更新引导

sudo make install

这个命令做了几件关键事情:

  1. 将内核镜像文件( bzImage )复制到 /boot/ 目录,通常命名为 vmlinuz-版本号 。
  2. 复制初始内存盘( initramfs 或 initrd )映像到 /boot/ 。这个映像包含了启动早期阶段所需的驱动和工具(比如加载真正根文件系统所需的驱动)。
  3. 更新系统引导程序(通常是GRUB)的配置文件,将新内核添加到启动菜单中。

make install 脚本内部会调用 update-grub (或 grub-mkconfig )来生成新的GRUB配置。完成后,你可以查看 /boot/grub/grub.cfg 文件,确认新内核的启动条目已经添加。

实操心得 :在某些发行版(如Arch Linux)或自定义程度较高的系统中, make install 可能不会自动配置引导。你需要手动将内核镜像和 initramfs 复制到 /boot ,并手动更新GRUB。这就是为什么理解每一步在做什么很重要,而不是死记命令。

5. 重启验证与故障排查指南

安装完成,最激动又紧张的时刻来了——重启并选择你的新内核。

5.1 重启与选择内核

sudo reboot

在系统重启,出现GRUB引导菜单时,通常需要按 Shift (BIOS系统)或 Esc (UEFI系统)来显示菜单。在菜单中,你应该能看到一个新的启动选项,名称里包含你编译的内核版本号(例如 “Ubuntu, with Linux 6.8.12-mycustomkernel”)。选择它并启动。

5.2 验证新内核成功运行

如果系统成功进入桌面或命令行,首先运行:

uname -r

这个命令会打印出当前运行内核的版本号。如果显示的是你自定义的版本号(如 6.8.12-mycustomkernel ),那么恭喜你,内核编译安装成功了!

你还可以使用 dmesg | head -20 查看内核启动日志的前几行,通常第一行就包含了内核版本和编译信息。

5.3 常见启动失败问题与解决方案

编译内核难免会遇到启动失败。别慌,大多数情况都有解决办法。请确保在重启前,旧内核仍然可以正常启动(即你有回退方案)。

问题1:黑屏,卡在 “Loading initial ramdisk...” 或类似信息之后。

  • 原因分析 :这通常意味着 initramfs 映像没有包含访问根文件系统所需的驱动程序(例如,你的根文件系统在LVM逻辑卷上,或者使用了特殊的硬件RAID控制器、NVMe驱动被编译成了模块但未包含进initramfs)。
  • 解决方案 :
    1. 重启进入旧内核。
    2. 检查你的根文件系统类型和所在设备。使用 lsblk -f 和 df -h / 。
    3. 确保在内核配置中,对应的驱动(如 CONFIG_NVME_CORE=y , CONFIG_SATA_AHCI=y , CONFIG_DM=y for LVM)被编译进了内核( =y ),而不是模块( =m )。如果是模块,它们需要被包含进 initramfs 。
    4. 更简单的方法是,在旧内核中,重新生成 initramfs 并确保它针对新内核。有时 make install 生成的 initramfs 可能不完整。可以尝试:
      cd /path/to/your/kernel/source
      sudo update-initramfs -c -k 6.8.12-mycustomkernel
      sudo update-grub
      
      然后再次重启尝试。

问题2:启动后无法进入图形界面(黑屏或卡在命令行)。

  • 原因分析 :显卡驱动(尤其是闭源的NVIDIA驱动)与新版内核不兼容,或者必要的图形驱动(如 CONFIG_DRM_AMDGPU 或 CONFIG_DRM_I915 )未被正确编译或加载。
  • 解决方案 :
    1. 重启进入旧内核。
    2. 如果使用NVIDIA闭源驱动,你需要为新内核重新安装或编译对应的NVIDIA内核模块。这通常可以通过 sudo apt install --reinstall nvidia-driver-xxx (Ubuntu)或重装 nvidia-dkms 包来完成。
    3. 检查内核配置中,你的显卡驱动是否启用。对于Intel集成显卡,确保 CONFIG_DRM_I915=y ;对于AMD显卡,确保 CONFIG_DRM_AMDGPU=y 。如果是模块,确保它们被正确加载。

问题3:GRUB菜单中没有出现新内核选项。

  • 原因分析 : make install 执行失败,或者GRUB配置未更新。
  • 解决方案 :
    1. 进入旧内核。
    2. 检查 /boot 目录下是否存在 vmlinuz-你的版本号 和 initrd.img-你的版本号 文件。
    3. 手动更新GRUB:
      sudo update-grub
      
    4. 再次检查 /boot/grub/grub.cfg ,搜索你的内核版本号是否出现。

问题4:内核恐慌(Kernel Panic),屏幕打印一堆错误信息后停止。

  • 原因分析 :内核在启动早期遇到了无法处理的严重错误。原因可能是关键驱动缺失、文件系统支持未编译、或者内核与硬件存在兼容性问题。
  • 解决方案 :这是最棘手的情况。仔细阅读恐慌信息的第一行和最后几行,它们通常指明了出错的模块或原因。根据错误信息回溯内核配置。最常见的解决方法是:回到配置步骤, 确保所有关键的系统驱动(如SATA控制器、USB主控制器、文件系统)都编译进了内核(=y) ,而不是模块。对于初次尝试,一个保守的策略是,在复用旧配置的基础上,只做“减法”(裁剪不需要的),尽量少做“加法”或修改核心部分。

6. 内核管理与后续维护

成功启动新内核只是开始,如何管理多个内核并保持系统清洁也很重要。

6.1 管理多个内核版本

你的系统现在至少有两个内核:旧的原装内核和新的自定义内核。GRUB菜单允许你选择启动哪个。为了安全,建议保留至少一个已知稳定的旧内核作为备份。

  • 查看已安装内核 :

    # 查看 /boot 下的内核镜像
    ls -lh /boot/vmlinuz-*
    # 查看已安装的内核模块目录
    ls -lh /lib/modules/
    
  • 删除旧内核(谨慎操作!) : 在确认新内核完全稳定工作后,可以清理旧内核以释放 /boot 分区空间(如果 /boot 是独立分区且空间紧张)。 注意 :务必确保你正在运行你想要保留的内核,并且至少保留一个可用的备用内核。 在Debian/Ubuntu上,可以使用:

    sudo apt autoremove --purge
    

    这会自动移除不再需要的旧内核包。或者,使用 sudo apt remove linux-image-旧版本号 手动删除。 在Arch Linux上,使用 sudo pacman -Rns linux-旧版本号 。 手动删除 :如果不通过包管理器,直接删除 /boot 下的相关文件并运行 sudo update-grub 是危险的,因为可能残留依赖关系。尽量使用系统包管理器进行清理。

6.2 内核的后续更新与补丁应用

  • 小版本更新 :如果你编译的是稳定版(如6.8.12),当6.8.13发布时,你可以下载新的源码包,将旧的 .config 文件复制到新源码目录,然后运行:

    make olddefconfig
    

    这个命令会基于旧配置自动回答新版本引入的新配置选项的默认值,非常方便。然后重新编译安装即可。

  • 应用补丁 :有时你需要应用一个特定的修复补丁。补丁文件通常以 .patch 结尾。在源码目录内,使用 patch 命令:

    # 假设补丁文件是 fix.patch
    patch -p1 < fix.patch
    

    -p1 参数表示忽略补丁文件路径中的第一级目录,这是从内核邮件列表下载补丁时的常见用法。

  • 版本控制 :强烈建议使用 git 管理你的内核源码树。这样你可以轻松地在不同版本间切换、打补丁和回溯更改。

    git checkout v6.8.12 # 切换到特定标签
    git apply my_custom_patch.patch # 应用补丁
    git diff # 查看本地修改
    

内核编译是一个实践性极强的技能,第一次成功会给你带来巨大的成就感。更重要的是,这个过程强迫你去理解系统底层各个组件是如何协同工作的。从此,你不再只是Linux的使用者,而是成为了它的塑造者之一。当系统出现深层次问题时,你拥有的排错能力和解决思路将远超从前。记住,最宝贵的经验往往来自于解决那些编译失败和启动黑屏的问题,每一次排查都是对系统理解的一次深化。

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐