如何快速构建精简的 Linux 内核

本指南介绍如何快速构建 Linux 内核,这些内核非常适合用于测试目的,但对于日常使用来说也完全没问题。

过程的本质(又名“TL;DR”)

[如果您是编译 Linux 的新手,请忽略此 TLDR 并转到下面的下一节:它包含一个循序渐进的指南,该指南更详细,但仍然简短且易于遵循;该指南及其随附的参考部分还提到了替代方案、陷阱和其他方面,所有这些都可能与您相关。]

如果您的系统使用 Secure Boot 之类的技术,请准备好允许启动自行编译的 Linux 内核;安装编译器和构建 Linux 所需的一切;确保您的主目录中有 12 GB 的可用空间。现在运行以下命令来下载新的 Linux 主线源码,然后使用这些源码来配置、构建和安装您自己的内核

git clone --depth 1 -b master \
  https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git ~/linux/
cd ~/linux/
# Hint: if you want to apply patches, do it at this point. See below for details.
# Hint: it's recommended to tag your build at this point. See below for details.
yes "" | make localmodconfig
# Hint: at this point you might want to adjust the build configuration; you'll
#   have to, if you are running Debian. See below for details.
make -j $(nproc --all)
# Note: on many commodity distributions the next command suffices, but on Arch
#   Linux, its derivatives, and some others it does not. See below for details.
command -v installkernel && sudo make modules_install install
reboot

如果您以后想构建更新的主线快照,请使用以下命令

cd ~/linux/
git fetch --depth 1 origin
# Note: the next command will discard any changes you did to the code:
git checkout --force --detach origin/master
# Reminder: if you want to (re)apply patches, do it at this point.
# Reminder: you might want to add or modify a build tag at this point.
make olddefconfig
make -j $(nproc --all)
# Reminder: the next command on some distributions does not suffice.
command -v installkernel && sudo make modules_install install
reboot

循序渐进指南

原则上,编译您自己的 Linux 内核很容易。有很多种方法可以做到这一点。哪些方法实际上有效并且是最好的取决于具体情况。

本指南介绍了一种非常适合那些想从源码快速安装 Linux 而不受复杂细节困扰的人的方法;目标是涵盖在运行于商品 PC 或服务器硬件上的主流 Linux 发行版上通常需要的一切。

所描述的方法非常适合用于测试目的,例如尝试建议的修复程序或检查问题是否已在最新的代码库中修复。尽管如此,以这种方式构建的内核对于日常使用也完全没问题,同时也很容易保持最新状态。

以下步骤描述了该过程的重要方面;后面的综合参考部分更详细地解释了其中的每一个步骤。它有时还会描述替代方法、陷阱以及可能在特定点发生的错误——以及如何重新开始。

  • 创建一个新的备份,并将系统修复和还原工具放在手边,以便为发生意外情况做好准备。

    [详情]

  • 在具有“安全启动”或类似技术的平台上,准备好一切,以确保系统以后允许您自行编译的内核启动。在商品 x86 系统上实现此目的的最快、最简单的方法是在 BIOS 设置实用程序中禁用此类技术;或者,通过 mokutil --disable-validation 启动的过程来删除其限制。

    [详情]

  • 安装构建 Linux 内核所需的所有软件。通常您需要:‘bc’、‘binutils’(‘ld’ et al.)、‘bison’、‘flex’、‘gcc’、‘git’、‘openssl’、‘pahole’、‘perl’ 以及 ‘libelf’ 和 ‘openssl’ 的开发头文件。参考部分显示了如何在各种流行的 Linux 发行版上快速安装这些文件。

    [详情]

  • 确保有足够的可用空间来构建和安装 Linux。对于后者,/lib/ 中 150 兆字节和 /boot/ 中 100 兆字节是一个安全的选择。对于存储源码和构建工件,您的主目录中通常应该有 12 GB 的空间。如果您的可用空间较少,请务必查看参考部分中解释调整内核构建配置的步骤:它提到了一个技巧,可以将 /home/ 中所需的空间减少到大约 4 GB。

    [详情]

  • 检索您要构建的 Linux 版本的源码;然后更改到包含它们的目录中,因为本指南中的所有其他命令都应从那里执行。

    [注意:以下段落描述了如何通过部分克隆 Linux 稳定 git 存储库来检索源码。这称为浅克隆。参考部分解释了两种替代方法: 打包的存档 完整的 git 克隆 ;如果下载大量数据不会困扰您,则首选后者,因为它将避免参考部分解释的浅克隆的一些 特殊特性 。]

    首先,执行以下命令来检索新的主线代码库

    git clone --no-checkout --depth 1 -b master \
      https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git ~/linux/
    cd ~/linux/
    

    如果您想访问最近的主线版本和预发布版本,请将克隆的历史记录加深到您感兴趣的最旧的主线版本

    git fetch --shallow-exclude=v6.0 origin
    

    如果您想访问稳定/长期版本(例如 v6.1.5),只需添加包含该系列的 branch;之后,至少获取到启动该系列的主线版本(v6.1)的历史记录

    git remote set-branches --add origin linux-6.1.y
    git fetch --shallow-exclude=v6.0 origin
    

    现在检出您感兴趣的代码。如果您刚刚执行了初始克隆,您将能够检出一个新的主线代码库,这对于检查开发人员是否已经修复了问题非常理想

    git checkout --detach origin/master
    

    如果您加深了克隆,则可以指定您加深到的版本(上面的 v6.0)而不是 origin/master;后面的版本(如 v6.1)和预发布版本(如 v6.2-rc1)也可以正常工作。如果按所述添加了适当的稳定/长期 branch,则稳定或长期版本(如 v6.1.5)的工作方式相同。

    [详情]

  • 如果您想应用内核补丁,请立即执行。通常这样的命令可以解决问题

    patch -p1 < ../proposed-fix.patch
    

    是否实际需要 -p1 取决于补丁的创建方式;如果它不适用,请尝试不使用它。

    如果您使用 git 克隆了源码,并且出现任何问题,请运行 git reset --hard 以撤消对源码的任何更改。

    [详情]

  • 如果您已修补了内核,或者已经安装了相同版本的内核,最好为要构建的内核添加一个唯一的标签

    echo "-proposed_fix" > localversion
    

    稍后在您的内核下运行 uname -r 将打印类似 “6.1-rc4-proposed_fix” 的内容。

    [详情]

  • 基于现有配置为您的内核创建构建配置。

    如果您已经自己准备了这样的 “.config” 文件,请将其复制到 ~/linux/ 并运行 make olddefconfig

    如果您的发行版或其他人已经根据您或您的硬件的需求定制了您正在运行的内核,请使用相同的命令:然后 make target “olddefconfig” 将尝试使用该内核的 .config 作为 base。

    对于其他人来说,使用此 make target 也可以,但您通常可以通过使用此命令来节省大量时间

    yes "" | make localmodconfig
    

    这将尝试选择您发行版的内核作为 base,然后为您的设置显然多余的任何功能禁用模块。这将大大减少编译时间,尤其是当您运行来自商品 Linux 发行版的通用内核时。

    有一个问题:“localmodconfig” 可能会禁用自您启动 Linux 以来未使用的内核功能——例如,当前断开连接的外围设备的驱动程序或尚未使用的虚拟化软件。您可以使用参考部分概述的技巧来减少或几乎消除这种风险;但是,当构建仅用于快速测试目的的内核时,缺少此类功能通常可以忽略不计。但是,在使用使用此 make target 构建的内核时,您应该牢记这一方面,因为它可能是您偶尔使用的某些功能停止工作的原因。

    [详情]

  • 检查您是否可能想要或必须调整一些内核配置选项

  • 评估您希望如何处理调试符号。如果以后可能需要解码堆栈跟踪(例如在 “panic”、“Oops”、“warning” 或 “BUG” 中找到的堆栈跟踪),请启用它们;另一方面,如果您存储空间不足或更喜欢较小的内核二进制文件,请禁用它们。有关如何执行这两种操作的详细信息,请参阅参考部分。如果两者都不适用,那么简单地不去管它可能就可以了。[详情]

  • 您是否正在运行 Debian?然后通过执行参考部分中解释的其他调整来避免已知问题。[详情]。

  • 如果您想影响配置的其他方面,请立即使用 “menuconfig” 或 “xconfig” 之类的 make target 来进行操作。[详情]。

  • 构建内核的镜像和模块

    make -j $(nproc --all)
    

    如果您希望将内核打包为 deb、rpm 或 tar 文件,请参阅参考部分中的替代方案。

    [详情]

  • 现在安装您的内核

    command -v installkernel && sudo make modules_install install
    

    通常,您之后要做的就是 reboot,因为许多商品 Linux 发行版会在您的引导加载程序的配置中创建一个 initramfs(也称为 initrd)和内核的条目;但是在某些发行版上,您必须手动执行这两个步骤,原因参考部分解释了。

    在少数发行版(如 Arch Linux 及其衍生产品)上,上面的命令根本不起作用;在这种情况下,您必须手动安装内核,如参考部分所述。

    如果您正在运行不可变的 Linux 发行版,请查看其文档和网络,以了解如何在其中安装您自己的内核。

    [详情]

  • 稍后要构建另一个内核,您需要类似的步骤,但有时需要略有不同的命令。

    首先,切换回源码树

    cd ~/linux/
    

    如果您想构建来自您尚未使用的稳定/长期系列的某个版本(例如 6.2.y),请告诉 git 跟踪它

    git remote set-branches --add origin linux-6.2.y
    

    现在获取最新的上游更改;您再次需要指定您关心的最早版本,因为 git 否则可能会检索整个提交历史

    git fetch --shallow-exclude=v6.0 origin
    

    现在切换到您感兴趣的版本——但请注意,此处使用的命令将丢弃您执行的任何修改,因为它们会与您要检出的源码冲突

    git checkout --force --detach origin/master
    

    此时,您可能想再次修补源码或设置/修改构建标签,如前所述。之后,使用 olddefconfig 将构建配置调整为新的代码库,这将使用您之前为下一个内核准备的 localmodconfig (~/linux/.config) 调整配置文件

    # reminder: if you want to apply patches, do it at this point
    # reminder: you might want to update your build tag at this point
    make olddefconfig
    

    现在构建您的内核

    make -j $(nproc --all)
    

    之后,如上所述安装内核

    command -v installkernel && sudo make modules_install install
    

    [详情]

  • 您的内核稍后很容易删除,因为它的各个部分仅存储在两个位置,并且可以通过内核的版本名称清楚地识别出来。只需确保不要删除您正在运行的内核,因为这可能会导致您的系统无法启动。

    首先删除包含内核模块的目录,该目录以其版本名称命名——在以下示例中为 “6.0.1-foobar”

    sudo rm -rf /lib/modules/6.0.1-foobar
    

    现在尝试以下命令,该命令将在某些发行版上删除所有其他已安装的内核文件,同时从引导加载程序配置中删除内核的条目

    command -v kernel-install && sudo kernel-install -v remove 6.0.1-foobar
    

    如果该命令没有任何输出或失败,请参阅参考部分;如果 /boot/ 中仍然存在任何名为“6.0.1-foobar”的文件,请执行相同的操作。

    [详情]

您在遵循上述任何步骤时是否遇到问题,而以下参考部分未清除这些问题?或者您是否有任何想法可以改进文本?然后请花一点时间,并通过电子邮件告知本文档的维护者(Thorsten Leemhuis <linux@leemhuis.info>),理想情况下,同时抄送 Linux 文档邮件列表(linux-doc@vger.kernel.org)。这种反馈对于进一步改进本文档至关重要,这符合每个人的利益,因为它将使更多人能够掌握此处描述的任务。

循序渐进指南的参考部分

本节包含上述指南中每个步骤的附加信息。

为紧急情况做好准备

创建一个新的备份,并将系统修复和还原工具放在手边 [...]

请记住,您正在处理计算机,计算机有时会做出意想不到的事情——特别是如果您摆弄操作系统的内核等关键部件。这就是您将在此过程中要做的事情。因此,最好为发生意外情况做好准备,即使这种情况不应该发生。

[返回循序渐进指南]

处理安全启动之类的技术

在具有“安全启动”或类似技术的平台上,准备好一切,以确保系统以后允许您自行编译的内核启动。 [...]

许多现代系统只允许某些操作系统启动;因此,默认情况下,它们将拒绝启动自行编译的内核。

理想情况下,您应该通过使您的平台信任您使用证书和签名自行构建的内核来解决此问题。此处未介绍如何执行此操作,因为它需要各种步骤,这些步骤会使文本偏离其目的太远;“内核模块签名工具”和各种网站已经更详细地解释了这一点。

暂时禁用安全启动之类的解决方案是使您自己的 Linux 启动的另一种方法。在商品 x86 系统上,可以在 BIOS 设置实用程序中执行此操作;此处未介绍执行此操作的步骤,因为它们在机器之间差异很大。

在主流 x86 Linux 发行版上,还有第三种通用选项:禁用 Linux 环境的所有安全启动限制。您可以通过运行 mokutil --disable-validation 来启动此过程;这将告诉您创建一个一次性密码,可以安全地将其写下来。现在重新启动;在您的 BIOS 执行完所有自检后,引导加载程序 Shim 将显示一个蓝色框,其中包含消息“按任意键执行 MOK 管理”。在倒计时暴露之前按一些键。这将打开一个菜单,并在其中选择“更改安全启动状态”。Shim 的 “MokManager” 现在会要求您输入之前指定的一次性密码中的三个随机选择的字符。提供这些字符后,确认您确实要禁用验证。之后,允许 MokManager 重新启动机器。

[返回循序渐进指南]

安装构建要求

安装构建 Linux 内核所需的所有软件。 [...]

内核非常独立,但除了编译器之类的工具外,您有时还需要一些库来构建内核。如何安装所需的一切取决于您的 Linux 发行版和您要构建的内核的配置。

以下是一些在一些主流发行版上通常需要的内容示例

  • Debian、Ubuntu 及其衍生产品

    sudo apt install bc binutils bison dwarves flex gcc git make openssl \
      pahole perl-base libssl-dev libelf-dev
    
  • Fedora 及其衍生产品

    sudo dnf install binutils /usr/include/{libelf.h,openssl/pkcs7.h} \
      /usr/bin/{bc,bison,flex,gcc,git,openssl,make,perl,pahole}
    
  • openSUSE 及其衍生产品

    sudo zypper install bc binutils bison dwarves flex gcc git make perl-base \
      openssl openssl-devel libelf-dev
    

如果您想知道为什么这些列表包含 openssl 及其开发头文件:它们是安全启动支持所必需的,许多发行版在其 x86 机器的内核配置中启用了安全启动支持。

有时您还需要用于压缩格式(如 bzip2、gzip、lz4、lzma、lzo、xz 或 zstd)的工具。

如果您执行本指南中未涵盖的任务,则可能需要其他库及其开发头文件。例如,当从 tools/ 目录构建内核工具时,将需要 zlib;使用 “menuconfig” 或 “xconfig” 之类的 make target 调整构建配置将需要 ncurses 或 Qt5 的开发头文件。

[返回循序渐进指南]

空间要求

确保有足够的可用空间来构建和安装 Linux。 [...]

提到的数字是粗略的估计值,并且有很大的额外费用,以确保安全,因此通常您需要的空间会更少。

如果您的空间有限,请记住在您到达 关于配置调整的部分 时阅读参考部分,因为确保禁用调试符号将减少相当数量的千兆字节的已用磁盘空间。

[返回循序渐进指南]

下载源码

检索您要构建的 Linux 版本的源码。 [...]

循序渐进指南概述了如何使用浅 git 克隆来检索 Linux 的源码。关于此方法还有 更多要说的,还有两种值得描述的替代方法:打包的存档完整的 git 克隆。以及方面“使用适当的预发布版本而不是最新的主线代码是否更明智”和“如何获得更新的主线代码库”也需要详细说明。

请注意,为了使事情简单,本指南中使用的命令将构建工件存储在源码树中。如果您希望分离它们,只需将类似 O=~/linux-builddir/ 的内容添加到所有 make 调用中;还要调整所有添加文件或修改任何生成的(例如您的 “.config”)文件的命令中的路径。

[返回循序渐进指南]

浅克隆的值得注意的特性

循序渐进指南使用浅克隆,因为它是本文档大多数目标受众的最佳解决方案。这种方法有几个值得一提的方面

  • 本文档在大多数地方使用带有 --shallow-exclude=git fetch 来指定您关心的最早版本(或者更准确地说:它的 git 标签)。您也可以使用参数 --shallow-since= 来指定绝对日期(例如 '2023-07-15')或相对日期('12 months')来定义您要下载的历史记录的深度。作为第二种替代方法,您还可以使用类似 --depth=1 的参数显式指定某个深度,除非您为稳定/长期内核添加 branch。

  • 运行 git fetch 时,请记住始终指定您关心的最旧版本、时间或显式深度,如循序渐进指南所示。否则,您将冒着下载几乎整个 git 历史记录的风险,这将消耗相当多的时间和带宽,同时也会给服务器带来压力。

    请注意,您不必一直使用相同的版本或日期。但是,当您随着时间的推移更改它时,git 会将历史记录加深或展平到指定的点。这使您可以检索最初认为不需要的版本——或者它将丢弃旧版本的源码,例如在您想释放一些磁盘空间的情况下。当使用 --shallow-since=--depth= 时,后者将自动发生。

  • 请注意,在加深克隆时,您可能会遇到类似 “fatal: error in object: unshallow cafecaca0c0dacafecaca0c0dacafecaca0c0da” 的错误。在这种情况下,运行 git repack -d 并重试``

  • 如果您想从某个版本(例如 Linux 6.3)恢复更改或执行二分查找 (v6.2..v6.3),最好告诉 git fetch 检索最多三个版本之前的对象(例如 6.0):git describe 之后将能够像在完整的 git 克隆中一样描述大多数提交。

[返回循序渐进指南] [返回部分介绍]

使用软件包存档下载源码

编译 Linux 的新手通常认为通过 https://linuxkernel.org.cn 的 front-page 下载存档是检索 Linux 源码的最佳方法。如果您确定只构建一个特定的内核版本而不更改任何代码,那么这实际上是可以的。问题是:您可能确信情况会是这样,但实际上它通常会被证明是错误的假设。

这是因为在报告或调试问题时,开发人员通常会要求尝试另一个版本。他们也可能会建议使用 git revert 暂时撤消提交,或者可能会提供各种补丁来尝试。有时还会要求报告者使用 git bisect 来查找导致问题的更改。这些事情依赖于 git,或者使用 git 处理起来更容易、更快捷。

浅克隆也不会增加任何显着的开销。例如,当您使用 git clone --depth=1 创建最新的主线代码库的浅克隆时,git 只会检索比通过 kernel.org 的 front-page 下载最新的主线预发布版本(又名“rc”)略多的数据。

因此,浅克隆通常是更好的选择。如果您仍然想使用打包的源码存档,请通过 kernel.org 下载一个存档;之后,将其内容提取到某个目录,然后更改为提取过程中创建的子目录。除了依赖 git 的内容外,循序渐进指南的其余部分都可以正常工作——但这主要涉及关于其他版本的连续构建的部分。

[返回循序渐进指南] [返回部分介绍]

使用完整的 git 克隆下载源码

如果下载和存储大量数据(截至 2023 年初,约为 4.4 千兆字节)不会困扰您,请执行完整的 git 克隆而不是浅克隆。然后,您将避免上面提到的特殊性,并且可以随时掌握所有版本和单个提交

curl -L \
  https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git/clone.bundle \
  -o linux-stable.git.bundle
git clone linux-stable.git.bundle ~/linux/
rm linux-stable.git.bundle
cd ~/linux/
git remote set-url origin \
  https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git
git fetch origin
git checkout --detach origin/master

[返回循序渐进指南] [返回部分介绍]

适当的预发布版本 (RC) 与最新的主线版本

当使用 git 克隆源码并检出 origin/master 时,您通常会检索到一个代码库,该代码库介于最新版本和下一个版本或预发布版本之间。这几乎总是您想尝试主线代码时想要的代码:v6.1-rc5 之类的预发布版本没有任何特殊之处,因为它们在发布之前不会进行任何重要的额外测试。

有一个例外:你可能希望坚持最新的主线版本(例如 v6.1),直到其后续版本的第一个预发布版(v6.2-rc1)发布。这是因为在此期间,编译器错误和其他问题更有可能发生,因为主线此时处于“合并窗口”:通常为期两周的阶段,其中下一个版本的大部分更改都会被合并。

[返回循序渐进指南] [返回部分介绍]

避免主线延迟

浅克隆和完整克隆的解释都从 Linux 稳定版 git 仓库检索代码。这使得本文档的读者更容易理解,因为它允许轻松访问主线和稳定/长期维护版本。这种方法只有一个缺点

合并到主线仓库的更改每隔几个小时才会同步到 Linux 稳定版仓库的 master 分支。这种延迟通常不必担心;但是,如果您真的需要最新的代码,只需将主线仓库添加为额外的远程仓库,然后从那里检出代码

git remote add mainline \
  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
git fetch mainline
git checkout --detach mainline/master

在使用浅克隆执行此操作时,请记住使用前面描述的参数之一调用 git fetch 以限制深度。

[返回循序渐进指南] [返回部分介绍]

修补源代码(可选)

如果您想应用内核补丁,请立即执行。 [...]

这是您可能需要修补内核的时候——例如,当开发人员提出了一个修复程序并要求您检查它是否有帮助时。逐步指南已经解释了这里的所有关键内容。

[返回逐步指南]

标记此内核构建(可选,通常是明智的)

如果您修补了内核或已经安装了该内核版本,最好通过扩展其发布名称来标记您的内核: [...]

标记内核将有助于避免以后出现混淆,尤其是在您修补了内核的情况下。添加单独的标签还将确保内核映像及其模块与任何现有内核并行安装。

有多种方法可以添加这样的标签。逐步指南通过在构建目录中创建一个“localversion”文件来实现这一点,内核构建脚本将自动从中拾取标签。您可以稍后更改该文件以在后续构建中使用不同的标签,或者直接删除该文件以转储标签。

[返回逐步指南]

定义内核的构建配置

基于现有配置为内核创建构建配置。 [...]

此步骤的各个方面需要更仔细的解释

使用另一个配置文件作为基础时的陷阱

像 localmodconfig 和 olddefconfig 这样的 make 目标有一些你想要注意的常见陷阱

  • 这些目标将重用构建目录中的内核构建配置(例如“~/linux/.config”),如果存在的话。如果您想从头开始,则需要删除它。

  • make 目标会尝试自动查找正在运行的内核的配置,但可能选择不佳。像 '# using defaults found in /boot/config-6.0.7-250.fc36.x86_64' 或 'using config: ‘/boot/config-6.0.7-250.fc36.x86_64’' 这样的行会告诉你它们选择了哪个文件。如果这不是预期的文件,只需在使用这些 make 目标之前将其存储为“~/linux/.config”。

  • 如果您尝试在旧版本(例如 v5.15)上使用为某个内核(例如 v6.0)准备的配置文件,则可能会发生意想不到的事情。在这种情况下,您可能需要使用一个配置作为基础,该配置是您的发行版在使用该内核或稍微旧的内核版本时使用的。

影响配置

make 目标 olddefconfig 和使用 localmodconfig 时使用的 yes "" | 会将任何未定义的构建选项设置为其默认值。这尤其会禁用在您的基本内核发布后引入的许多内核功能。

如果您想手动设置这些配置选项,请使用 oldconfig 而不是 olddefconfig,或者在使用 localmodconfig 时省略 yes "" |。然后,对于每个未定义的配置选项,您都会被问到如何进行。如果您不确定如何回答,只需按“Enter”键即可应用默认值。

使用 localmodconfig 时的大陷阱

正如逐步指南中已经简要解释的那样:使用 localmodconfig 很容易发生您自己构建的内核会缺少您在使用此 make 目标之前未执行的任务的模块。 这是因为这些任务需要内核模块,这些模块通常在您第一次执行该任务时会自动加载; 如果您在使用 localmodconfig 之前至少没有执行过该任务一次,则后者会认为这些模块是多余的并禁用它们。

您可以尝试通过执行通常会自动加载其他内核模块的典型任务来避免这种情况:启动 VM,建立 VPN 连接,循环挂载 CD/DVD ISO,挂载网络共享(CIFS、NFS、...),并连接所有外部设备(2FA 密钥、耳机、网络摄像头、...)以及具有您通常不使用的文件系统的存储设备(btrfs、ext4、FAT、NTFS、XFS、...)。 但很难想到可能需要的一切 - 即使内核开发人员也经常在此处忘记一件事或另一件事。

不要让这种风险困扰您,尤其是在仅出于测试目的编译内核时:通常至关重要的一切都会存在。 如果您忘记了重要的内容,您可以稍后打开缺少的功能,并快速运行命令来编译和安装更好的内核。

但是,如果您计划定期构建和使用自己构建的内核,您可能希望通过记录系统在几周内加载的模块来降低风险。您可以使用 modprobed-db 自动化此过程。 之后,使用 LSMOD=<path> 将 localmodconfig 指向 modprobed-db 注意到的正在使用的模块列表

yes "" | make LSMOD="${HOME}"/.config/modprobed.db localmodconfig

使用 localmodconfig 进行远程构建

如果您想使用 localmodconfig 为另一台机器构建内核,请在其上运行 lsmod > lsmod_foo-machine,并将该文件传输到您的构建主机。 现在将构建脚本指向该文件,如下所示:yes "" | make LSMOD=~/lsmod_foo-machine localmodconfig。 请注意,在这种情况下,您可能也想从另一台机器复制一个基本的内核配置,并将其作为 .config 放置在您的构建目录中。

[返回逐步指南]

调整构建配置

检查您是否可能想要或必须调整一些内核配置选项

根据您的需要,此时您可能想要或必须调整一些内核配置选项。

调试符号

评估您希望如何处理调试符号。 [...]

大多数用户不需要关心这个问题,通常可以保持原样;但是如果您可能需要解码堆栈跟踪或想要减少空间消耗,则应该仔细查看此内容。

当内核在运行时抛出“panic”、“Oops”、“warning”或“BUG”时,拥有可用的调试符号可能很重要,因为这样您就可以找到代码中发生问题的确切位置。 但是,收集和嵌入所需的调试信息需要时间并消耗相当多的空间:在 2022 年末,配置为 localmodconfig 的典型 x86 内核的构建工件在使用调试符号时消耗了大约 5 GB 的空间,但在禁用它们时消耗的空间不到 1 GB。 生成的内核映像和模块也更大,这会增加加载时间。

因此,如果您想要一个小的内核并且不太可能以后解码堆栈跟踪,您可能需要禁用调试符号以避免上述缺点

./scripts/config --file .config -d DEBUG_INFO \
  -d DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT -d DEBUG_INFO_DWARF4 \
  -d DEBUG_INFO_DWARF5 -e CONFIG_DEBUG_INFO_NONE
make olddefconfig

另一方面,如果很有可能以后需要解码堆栈跟踪(如 受污染的内核 中的“解码失败消息”更详细地解释的那样),那么您肯定希望启用它们

./scripts/config --file .config -d DEBUG_INFO_NONE -e DEBUG_KERNEL
  -e DEBUG_INFO -e DEBUG_INFO_DWARF_TOOLCHAIN_DEFAULT -e KALLSYMS -e KALLSYMS_ALL
make olddefconfig

请注意,许多主流发行版在其内核配置中启用了调试符号 - 因此像 localmodconfig 和 olddefconfig 这样的 make 目标通常会拾取该设置。

[返回逐步指南]

特定于发行版的调整

您正在运行 [...]

以下部分帮助您避免在一些常用发行版上遵循本指南时已知会发生的构建问题。

Debian

  • 删除对证书文件的过时引用,该引用会导致您的构建失败

    ./scripts/config --file .config --set-str SYSTEM_TRUSTED_KEYS ''
    

    或者,下载所需的证书并使该配置选项指向它,如 Debian 手册更详细地解释的那样 - 或者生成您自己的证书,如 内核模块签名工具 中解释的那样。

[返回逐步指南]

个别调整

如果您想影响配置的其他方面,请立即执行 [...]

此时您可以使用像 make menuconfig 这样的命令来使用基于文本的用户界面启用或禁用某些功能; 要使用图形配置实用程序,请改用 make 目标 xconfiggconfig。 所有这些都需要基于的工具包(ncurses、Qt5、Gtk2)的开发库; 如果缺少某些必需的库,则会显示一条错误消息。

[返回逐步指南]

构建你的内核

构建内核的映像和模块 [...]

在此阶段可能会发生很多错误,但以下说明将帮助您自助。 另一个小节解释了如何将内核直接打包为 deb、rpm 或 tar 文件。

处理构建错误

当发生构建错误时,可能是由您机器设置的某些方面引起的,这些方面通常可以快速修复; 但是,其他时候,问题在于代码中,只能由开发人员修复。 仔细检查失败消息并结合在互联网上进行一些研究通常会告诉您是哪一种。 要执行这样的调查,请像这样重新启动构建过程

make V=1

V=1 激活详细输出,这可能需要查看实际错误。 为了更容易发现,此命令还省略了 -j $(nproc --all) 以前使用的命令,以利用系统中用于该作业的每个 CPU 内核 - 但当发生故障时,这种并行性也会导致一些混乱。

几秒钟后,构建过程应再次遇到错误。 现在尝试找到描述问题的最关键的一行。 然后在互联网上搜索该行中最重要和非通用的部分(例如 4 到 8 个字); 避免或删除任何看起来与系统相关的东西,例如您的用户名或像 /home/username/linux/ 这样的本地路径名。 首先使用该字符串通过常规互联网搜索引擎进行搜索,然后通过 lore.kernel.org/all/ 搜索 Linux 内核邮件列表。

大多数情况下,这将找到一些可以解释错误原因的内容; 很多时候,其中一个点击也会为您的问题提供解决方案。 如果您没有找到任何与您的问题匹配的内容,请通过修改搜索词或使用错误消息中的另一行,从不同的角度再次尝试。

最后,您遇到的绝大多数问题可能已经被其他人遇到并报告过了。 这包括原因不是您的系统,而是代码的问题。 如果您遇到其中一种情况,您也可能会找到针对您的问题的解决方案(例如补丁)或解决方法。

打包你的内核

逐步指南使用默认的 make 目标(例如 x86 上的“bzImage”和“modules”)来构建内核的映像和模块,该指南的后续步骤会安装这些映像和模块。 您也可以使用以下目标直接构建所有内容并直接打包:

  • make -j $(nproc --all) bindeb-pkg 生成 deb 包

  • make -j $(nproc --all) binrpm-pkg 生成 rpm 包

  • make -j $(nproc --all) tarbz2-pkg 生成 bz2 压缩 tarball

这只是用于此目的的可用 make 目标的选择,请参阅 make help 以了解其他目标。 您也可以在运行 make -j $(nproc --all) 后使用这些目标,因为它们会拾取已构建的所有内容。

如果您使用目标来生成 deb 或 rpm 包,请忽略逐步指南中有关安装和删除内核的说明; 而是使用格式的包实用程序(例如 dpkg 和 rpm)或构建在其上的包管理实用程序(apt、aptitude、dnf/yum、zypper、...)来安装和删除包。 请注意,使用这两个 make 目标生成的包旨在在利用这些格式的各种发行版上工作,因此它们的行为有时会与发行版的内核包不同。

[返回逐步指南]

安装你的内核

现在安装你的内核 [...]

在逐步指南中执行命令后需要执行的操作取决于 installkernel 可执行文件是否存在及其实现。 许多常用的 Linux 发行版在 /sbin/ 中提供了一个这样的内核安装程序,它可以完成所需的一切,因此除了重新启动之外,您无需执行任何操作。 但是,某些发行版包含仅完成部分作业的 installkernel - 还有一些完全缺少它,并将所有工作都留给您。

如果找到 installkernel,内核的构建系统会将内核映像和相关文件的实际安装委托给此可执行文件。 在几乎所有 Linux 发行版上,它会将映像存储为“/boot/vmlinuz- ”,并在其旁边放置一个“System.map-”。 因此,除非您已经有一个具有完全相同的发布名称的内核,否则您的内核将与任何现有内核并行安装。

许多发行版上的 Installkernel 之后会生成一个“initramfs”(通常也称为“initrd”),常用发行版依赖它进行引导; 因此,请务必保持逐步指南中使用的两个 make 目标的顺序,因为如果在内核的模块之前安装其映像,事情会出错。 通常,installkernel 也会将您的内核添加到引导加载程序配置中。 如果您的发行版的 installkernel 不处理它们,您必须自己处理这些任务中的一项或两项。

像 Arch Linux 及其衍生产品一样,一些发行版完全缺少 installkernel 可执行文件。 在这些发行版上,只需使用内核的构建系统安装模块,然后手动安装映像和 System.map 文件

sudo make modules_install
sudo install -m 0600 $(make -s image_name) /boot/vmlinuz-$(make -s kernelrelease)
sudo install -m 0600 System.map /boot/System.map-$(make -s kernelrelease)

如果您的发行版在 initramfs 的帮助下启动,请现在使用您的发行版为此过程提供的工具为您的内核生成一个。 之后,将您的内核添加到引导加载程序配置并重新启动。

[返回逐步指南]

稍后进行另一轮

要稍后构建另一个内核,您需要类似但有时略有不同的命令 [...]

构建稍后内核的过程类似,但在某些方面略有不同。 例如,您不希望为后续内核构建使用“localmodconfig”,因为您已经创建了一个精简的配置,您希望从现在开始使用该配置。 因此,只需使用 oldconfigolddefconfig 来根据您即将构建的内核版本的需要调整您的构建配置。

如果您使用 git 创建了浅克隆,请记住详细解释设置的部分:您需要使用略有不同的 git fetch 命令,并且在切换到另一个系列时需要添加一个额外的远程分支。

[返回逐步指南]

稍后卸载内核

已安装内核的所有部分都可以通过其发布名称识别,因此以后很容易删除。 [...]

不要担心手动安装内核并因此绕过发行版的打包系统会完全搞乱您的机器:内核的所有部分都很容易在以后删除,因为文件仅存储在两个位置,并且通常可以通过内核的发布名称识别。

其中一个位置是 /lib/modules/ 中的目录,其中包含每个已安装内核的模块。 此目录以内核的发布名称命名; 因此,要删除某个内核的所有模块,只需删除 /lib/modules/ 中的模块目录即可。

另一个位置是 /boot/,在内核安装期间通常会在其中放置一到五个文件。 所有这些文件通常都在其文件名中包含发布名称,但是有多少文件及其名称在某种程度上取决于发行版的 installkernel 可执行文件(见上文)及其 initramfs 生成器。 在某些发行版上,逐步指南中提到的 kernel-install 命令会为您删除所有这些文件 - 同时也会删除引导加载程序配置中您的内核的条目。 在其他发行版上,您必须自己处理这些步骤。 以下命令应以交互方式删除发布名称为“6.0.1-foobar”的内核的两个主要文件

rm -i /boot/{System.map,vmlinuz}-6.0.1-foobar

现在删除所属的 initramfs,它通常被称为类似 /boot/initramfs-6.0.1-foobar.img/boot/initrd.img-6.0.1-foobar。 之后,检查 /boot/ 中是否有名称中包含“6.0.1-foobar”的其他文件,并将它们也删除。 现在从引导加载程序的配置中删除内核。

请注意,在手动删除内核的文件或目录时,要非常小心使用像“*”这样的通配符:当您只想删除 6.0 或 6.0.1 时,可能会意外删除 6.0.11 内核的文件。

[返回逐步指南]

常见问题解答

为什么这个“操作指南”在我的系统上不起作用?

正如最初声明的那样,本指南“旨在涵盖在运行在常用 PC 或服务器硬件上的主流 Linux 发行版上[构建内核]通常需要的所有内容”。 尽管如此,概述的方法也应该在许多其他设置上有效。 但是,尝试在一个指南中涵盖每种可能的用例都会适得其反,因为如果没有这样的重点,您将需要沿着“如果您有 <插入机器或发行版>,您此时必须执行 <此和彼> <代替|另外>”的思路构建数十或数百个构造。 每一种都会使文本更长、更复杂且更难遵循。

话虽如此:这当然是一种平衡行为。 因此,如果您认为其他用例值得描述,请将其建议给本文档的维护者,如上文所述