跳到内容

概述

在本指南中,我们将逐步介绍获取内核源代码树、配置它、编译它以及安装和启动它的过程。

不建议也不支持对 Rocky Linux 进行内核重建。在尝试构建自定义内核之前,请考虑以下事项

  • 您需要的功能是否可以通过从 elrepo 安装内核模块来实现?
  • 您需要的功能是否作为内核本身的独立模块提供?
  • Rocky Linux 和大多数其他 EL 派生版被设计为一个完整的环境。替换关键组件会影响系统的工作方式。
  • 大多数用户不再需要构建自己的内核。您可能只需要一个内核模块/驱动程序,或者可能构建自己的内核模块(kmod/dkms)

最后警告:如果您破坏了内核,您将负责解决系统上由此产生的问题。

内核

通常,当人们说“Linux”时,他们通常指的是“Linux 发行版” - 例如,Rocky Linux 和 Debian 是 Linux 发行版的类型。发行版包含让 Linux 作为功能齐全的操作系统存在所需的一切。发行版利用来自各种独立于 Linux 的开源项目的代码。

Linux 是内核。内核实际上位于 [操作系统] 事务的核心。

比内核更基本的东西只有系统硬件本身。虽然内核是完整 Linux 发行版中很小的一部分,但它是迄今为止最关键的元素。如果内核出现故障或崩溃,系统其余部分也会随之消失。

内核源代码

Rocky Linux 发行版以某种形式提供对其支持的特定内核版本的源代码。这些可能是以编译后的二进制文件(*.src.rpm)、源 RPM(*.srpm)等形式存在。

如果您需要下载与特定 Rocky Linux 发行版提供的版本不同的(可能更新的)版本,那么第一个查找源代码的地方是官方内核网站

www.kernel.org

该网站维护着内核源代码镜像网站的列表,以及大量其他开源软件、发行版和通用实用程序。

镜像列表维护在

mirrors.kernel.org

提示

以下各节中完成的大多数 Linux 内核下载、配置和编译工作可以/应该以非特权用户身份完成。但是,需要实际安装或更改系统文件和二进制文件的最后几个步骤需要以提升的权限完成。

我们能够以非特权用户身份完成大部分工作,因为我们将使用一个特殊的内核构建选项,该选项允许我们指定一个自定义的工作或输出目录。具体来说,我们将为所有适用的 make 调用使用 O=~/build/kernel 选项。

其中 ~/build/kernel 等效于 /home/$USER/build/kernel$HOME/build/kernel

内核版本和命名约定

可用的内核列表网站将包含 v1.0、v2.5、v2.6、v3.0、v3.x、v4.x、v5.x、v6.x 等等的文件夹。在您按照本能获取最新版本之前,请确保您了解 Linux 内核版本控制系统的工作原理。

当前的约定是将主要的新内核版本命名和编号为“Linux 5.x”(也称为 vanilla 或 mainline 内核)。因此,该系列的第一个版本将是 Linux 版本 5.0(与 5.0.0 相同),下一个版本将是 Linux 版本 5.1(与 5.1.0 相同),然后是 Linux 版本 5.2,依此类推。

每个主要发行版本中的任何次要更改或更新都将通过第三个数字的增量来反映。这些通常被称为稳定点版本。因此,Linux 版本 5.0.0 系列内核的下一个稳定点版本将是 Linux 版本 5.0.1,然后是版本 5.0.2,依此类推。换句话说,例如,Linux 版本 5.0.4 是基于 Linux 5.0.0 系列的第四个稳定版本。

安装先决条件工具和库

内核构建过程中遇到的常见错误可能是由于没有为编译和构建 mainline Linux 内核提供所有必要的软件。可以使用 Rocky Linux 发行版上的 DNF 包管理器安装缺少的工具和库。我们将在本节中处理这个问题。

  1. 在 Rocky Linux 发行版上,您可以通过运行以下命令快速安装大多数必要的开发工具

    sudo dnf -y groupinstall 'C Development Tools and Libraries'
    

    如果您收到“模块或组“C 开发工具和库”不可用。”错误,以下命令等同于上述命令

    sudo dnf -y groupinstall 'Development Tools'
    
  2. 一些其他库、头文件和应用程序也可以通过安装以下软件包获得。输入

    sudo dnf -y install ncurses-devel openssl-devel elfutils-libelf-devel python3
    
  3. 接下来,我们需要其他仅在某些支持的第三方存储库中可用的实用程序。其中一个这样的存储库是 Powertools 存储库。让我们在我们的 Rocky 系统上启用该存储库。输入

    sudo dnf config-manager --set-enabled powertools
    
  4. 最后,让我们从 Powertool 存储库安装一个需要的软件包。输入

    sudo  dnf -y install dwarves
    

这就是构建内核所需的先决条件软件包!

下载和解压缩 Linux 内核

我们将在下一节中构建的内核版本是版本 6.5.7,可在以下位置获取

www.kernel.org/pub/linux/kernel/v6.x/linux-6.5.7.tar.xz

让我们开始这个过程。

  1. 首先,使用以下 curl 命令将内核源代码下载到您的当前工作目录。输入

    curl -L -o linux-6.5.7.tar.xz \
    https://linuxkernel.org.cn/pub/linux/kernel/v6.x/linux-6.5.7.tar.xz
    
  2. 您将从互联网下载的内核源代码是一个已压缩和打包的文件。因此,您需要解压缩和解压缩源文件才能使用该源代码。

    确保您位于将内核 tar 包下载到的目录中。使用 tar 命令解压缩和解压缩文件,运行

    tar xvJf linux-6.*.tar.xz
    

构建内核

在本节中,我们将回顾配置和构建内核的过程。这与 macOS 或基于 Windows 的操作系统形成对比,后者是预先配置的,因此包含对您可能想要或可能不需要的许多功能的支持。

Linux 的设计理念允许个人决定内核的重要部分。这种个性化的设计具有重要的优势,可以让你缩减功能列表,以便 Linux 可以尽可能高效地运行。

这也是可以自定义 Linux 以在各种硬件设置中运行的原因之一,从低端系统到嵌入式系统再到高端系统。

构建内核需要两个主要步骤

  • 配置
  • 编译

构建内核的第一步是配置其功能。通常,您所需的特征列表将基于您需要支持的硬件。当然,这意味着您将需要一个硬件列表。

在已经运行 Linux 的系统上,您可以运行诸如 lspcilshw 等命令,以帮助显示有关系统上确切硬件设置的详细信息。在基于 RPM 的发行版上,这些实用程序由 pciutils*.rpmlshw*.rpm 软件包提供。

更好地了解构成您的底层硬件的内容可以帮助您更好地确定自定义内核中需要什么。您已准备好开始配置内核。

清理构建环境

我们可以从对新内核需要支持的硬件类型和功能的粗略了解开始实际配置。但首先,一些背景信息。

Linux 内核源代码树包含多个名为 Makefile 的文件(Makefile 只是一个包含指令的文本文件,它还描述了程序中文件之间的关系)。

这些 Makefile 有助于将构成内核源代码的数千个其他文件粘合在一起。这里对我们更重要的是 Makefile 还包含目标。目标是 make 程序执行的命令或指令。

警告:避免不必要的内核升级

请记住,如果您有一个稳定且运行良好的工作系统,除非以下条件之一适用于您,否则没有理由升级内核

  • 安全或错误修复会影响您的系统,必须应用
  • 您需要稳定版本中的特定新功能

在安全修复的情况下,请决定风险是否真的影响您 - 例如,如果安全问题出现在您未使用的设备驱动程序中,则可能没有理由升级。在错误修复版本的情况下,请仔细阅读发行说明并决定错误是否真的影响您 - 如果您有一个稳定的系统,使用从未使用过的补丁升级内核可能毫无意义。

在生产系统上,不应简单地升级内核只是为了拥有“最新内核”;您应该有充分的理由升级。

内核源代码树根目录中的 Makefile 包含特定目标,这些目标可用于准备内核构建环境、配置内核、编译内核、安装内核等。以下将更详细地讨论一些目标

make mrproper

此目标清理构建环境中可能来自先前内核构建的任何陈旧文件和依赖项。所有先前的内核配置都将从构建环境中清除(删除)。

make clean

此目标的工作不像 mrproper 目标那样彻底。它只删除大多数生成的文件。它不会删除内核配置文件(.config)。

make menuconfig

此目标调用一个基于文本的编辑器界面,其中包含菜单、选项列表和基于文本的对话框,用于配置内核。

make xconfig

此基于 GUI 的内核配置工具/目标依赖于 Qt 图形开发库。基于 KDE/Plasma 的应用程序使用这些库。

make gconfig

这也是一个基于 GUI 的内核配置工具/目标,但依赖于 GTK+ 工具包。此 GTK+ 工具包在 GNOME 桌面世界中被大量使用。

make olddefconfig

此目标使用当前工作目录中现有的 .config 文件,更新依赖项,并将新符号自动设置为其默认值。

make help

此目标将向您显示所有可能的 target,并作为快速在线帮助系统。

在本节中,我们将只使用其中一个 target 来配置内核。具体来说,我们将使用 make menuconfig 命令。menuconfig 内核配置编辑器是一个简单而流行的基于文本的配置实用程序,它包含菜单、单选按钮列表和对话框。

它有一个简单干净的界面,可以用键盘轻松导航,并且几乎是直观的。

我们需要更改 (cd) 到内核源代码目录,然后才能开始内核配置。但在开始实际内核配置之前,您应该使用 make mrproper 命令清理(准备)内核构建环境

cd linux-6.*
make  O=~/build/kernel mrproper

内核配置

接下来,我们将开始配置一个 Linux 6.* 系列内核。为了探索此过程的一些内部内容,我们将启用特定功能的支持,我们将假装该功能是系统上必须具备的功能。一旦您了解了它的工作原理,您就可以应用相同的步骤来添加对您想要的任何新内核功能的支持。具体来说,我们将启用对自定义内核中 NTFS 文件系统的支持。

大多数现代 Linux 发行版附带一个内核配置文件,该配置文件用于在本地文件系统上运行的内核,作为压缩文件或普通文件。在我们的示例 Rocky 系统上,此文件位于 /boot 目录中,通常命名为类似 config-*

配置文件包含为其表示的特定内核启用的选项和功能列表。我们通过配置内核的过程旨在创建与之类似的配置文件。我们创建的文件与现成的文件之间的唯一区别在于,我们将对自己的文件进行进一步的微调。

提示

使用已知的预先存在的配置文件作为创建我们自己的自定义文件的框架有助于确保我们不会浪费太多时间重复其他人已经投入到寻找有效内容和无效内容的努力中!

以下步骤介绍了如何配置内核。我们将使用基于文本的内核配置实用程序,这将允许您在终端中跟踪您的操作,无论您是否正在使用 GUI 桌面环境。

  1. 首先,我们将复制并重命名 /boot 目录中的预先存在的配置文件到我们的内核构建环境中

    cp /boot/config-`uname -r` ~/build/kernel/.config
    

    我们在这里使用 uname -r 来帮助我们获取运行内核的配置文件。uname -r 命令打印运行内核的版本。使用它有助于确保我们获得我们想要的准确版本,以防存在其他版本。

    注意

    Linux 内核配置编辑器开始明确查找并生成一个名为 .config(发音为“点配置”)的文件,该文件位于内核源代码树的根目录。此文件是隐藏的。

  2. 启动图形内核配置实用程序

    make O=~/build/kernel menuconfig
    

    将出现类似于此的屏幕

    Main Kernel Configuration screen

    出现的内核配置屏幕大致分为三个区域。顶部显示有用的信息、键盘快捷键和图例,以帮助您导航应用程序。屏幕的主体显示可展开的树状结构的内核选项列表。您可以进一步使用父级中的箭头深入查看项目以查看和/或配置子菜单(或子)项目。最后,屏幕底部显示用户可以选择的操作/选项。

  3. 接下来,我们将为演示目的将对 NTFS 的支持添加到我们的自定义内核中。

    在主配置屏幕上,使用箭头键导航到并突出显示文件系统项。选择文件系统后,按 Enter 键以查看文件系统的子菜单或子项。

    在文件系统部分,使用箭头键导航到 DOS/FAT/NT 文件系统。按 Enter 键查看 DOS/FAT/NT 文件系统的子项。

  4. 在 DOS/FAT/NT 文件系统部分,导航到 NTFS 文件系统支持。

    键入 M(大写)以启用模块以支持 NTFS 文件系统。

    使用箭头键向下导航到 NTFS 调试支持 (NEW),然后按 y 以将其包含在内。

    使用箭头键向下导航到 NTFS 写入支持,然后按 Y 以将其包含在内。完成后,每个选项旁边应该出现字母 M 或星号 (*) 符号,如下所示

    Kernel Configuration File Systems screen

    提示

    对于每个可配置选项,在内核配置实用程序中,空尖括号,<>,表示所讨论的功能已禁用。尖括号中的字母 M,表示该功能将作为模块进行编译。

    尖括号中的星号符号,<*>,表示对该功能的支持将直接构建到内核中。您通常可以使用键盘上的空格键在所有可能的选项之间切换。

  5. 在 DOS/FAT/NT 文件系统屏幕中,按键盘上的 esc 键两次返回到父文件系统屏幕。在键盘上再次按 esc 键两次返回到主内核配置屏幕。

  6. 最后,将更改保存到内核源代码树根目录下的 .config 文件中,并在保存文件后按键盘上的 esc 键两次退出内核配置应用程序。

  7. 将出现一个对话框,提示您保存新的配置。请确保选择“是”,然后按回车键。

  8. 内核配置工具退出后,您将被返回到内核源代码树中的 shell。您几乎可以开始构建您的内核了!

  9. 我们需要在 Rocky 发行版上完成一些额外的自定义。输入

    sed -ri '/CONFIG_SYSTEM_TRUSTED_KEYS/s/=.+/=""/g' ~/build/kernel/.config
    

    提示

    要查看使用 menuconfig 工具进行的一些更改的结果,请使用 grep 实用程序查看直接保存的 .config 文件。例如,要查看之前启用的 NTFS 文件系统支持的效果,请键入以下命令

    $ grep NTFS ~/build/kernel/.config
    CONFIG_NTFS_FS=m
    CONFIG_NTFS_DEBUG=y
    CONFIG_NTFS_RW=y
    

    关于内核模块的快速说明

    可加载模块支持是 Linux 内核的一项功能,它允许动态加载(或删除)内核模块。

    内核模块是已编译的代码片段,可以动态插入到正在运行的内核中,而不是永久构建到内核中。因此,很少使用的功能可以启用,但它们在未使用时不会占用任何内存空间。

    值得庆幸的是,Linux 内核可以自动确定加载什么以及何时加载。当然,并非所有功能都有资格编译为模块。内核必须知道一些信息才能加载和卸载模块,例如如何访问硬盘以及解析存储可加载模块的文件系统。一些内核模块也通常被称为驱动程序。

编译内核

在上一节中,我们介绍了创建要构建的自定义内核的配置文件的过程。在本节中,我们将执行内核的实际构建。但在执行此操作之前,我们将对整个过程添加一个更简单的自定义。

最终的自定义将是在内核的最终名称中添加一个额外的信息片段。这将帮助我们能够区分此内核与具有相同版本号的任何其他内核。我们将在内核版本信息中添加标签“custom”。这可以通过编辑主 Makefile 并将我们想要的标签附加到 EXTRAVERSION 变量来完成。

内核构建过程的编译阶段是迄今为止最简单的,但也需要花费最长时间。此时只需要执行 make 命令,该命令将自动生成并处理任何依赖项问题,编译内核本身,并编译任何启用为可加载模块的功能(或驱动程序)。

由于需要编译大量代码,请准备好等待几分钟,至少取决于您的系统的处理能力。让我们深入了解编译新内核所需的具体步骤。

  1. 首先,我们将为即将构建的内核添加一个额外的标识字符串。在内核源代码树的根目录中,我们将使用 sed 实用程序就地编辑 Makefile。我们要更改的变量位于文件的最顶部附近。我们想要更改文件中看起来像这样的行

    EXTRAVERSION =
    

    为此

    EXTRAVERSION = -custom
    

    使用以下 sed 命令进行更改。输入

    sed  -i 's/^EXTRAVERSION.*/EXTRAVERSION = -custom/'  Makefile
    

    当然,您也可以使用任何您熟悉的文本编辑器进行更改。只需记住将更改保存到文件中!

  2. 将 kernelversion 目标传递给 make 命令以查看刚自定义的内核的完整版本

    make O=~/build/kernel kernelversion
    

    提示

    您可以利用大多数现代系统上的所有额外处理能力(CPU、内核等),并大大加快 CPU 密集型操作(如编译内核)的速度。为此,您可以向 make 命令传递一个参数,该参数指定要同时运行的作业数。然后,指定的作业数将被分布并同时在每个 CPU 内核上执行。该命令的语法为

    ```bash
    make -j N
    ```
    where N is the number of jobs to run simultaneously. For example, if you have a octa (8) core–capable CPU, you can type:
    
    ```bash
    make -j 8
    ```
    
  3. 这里唯一需要的命令是 make 命令

    $ make  O=~/build/kernel
    make[1]: Entering directory '/home/super/build/kernel'
    SYNC    include/config/auto.conf.cmd
    GEN     Makefile
    HOSTCC  scripts/kconfig/conf.o
    HOSTLD  scripts/kconfig/conf
    GEN     Makefile
    ...<OUTPUT TRUNCATED>…
    LD [M]  sound/usb/usx2y/snd-usb-usx2y.ko
    LD [M]  sound/x86/snd-hdmi-lpe-audio.ko
    LD [M]  sound/xen/snd_xen_front.ko
    LD [M]  virt/lib/irqbypass.ko
    make[1]: Leaving directory '/home/super/build/kernel'
    
  4. 该命令的最终产品(即内核)位于路径中

    ~/build/kernel/arch/x86/boot/bzImage
    
  5. 我们需要安装模块,因为我们已将内核的一部分编译为模块(例如,NTFS 模块)。键入以下命令

    sudo make O=~/build/kernel modules_install
    

    在我们的 Rocky 系统上,此命令将所有已编译的内核模块安装到 /lib/modules/<new_kernel-version> 目录中。此路径在本示例中将转换为 /lib/modules/6.5.7-custom/。这是内核根据需要加载所有可加载模块的路径。

    提示

    通过 make modules_install 安装的内核模块的占位符(大小)可能非常大,因为模块包含调试符号。因此,您很容易最终得到一个 /lib/modules/6.5.7-custom/ 目录,其大小接近 5GB!

    在本指南中,我们通过在 make modules_install 调用中包含 INSTALL_MOD_STRIP=1 选项来避免这种大型尺寸。您可以通过剥离这些调试符号将总大小缩小几个数量级(例如,小于 200 MB!!)。

    这可以通过在 make modules_install 命令中包含 INSTALL_MOD_STRIP=1 选项来完成。

安装内核

假设您有一台电脑并且正在 ~/build/kernel/ 目录中工作,则在上一个练习中创建的已编译内核将位于此路径中 - <kernel-build-dir>/arch/x86/boot/bzImage 或,更准确地说,在我们示例中 ~/build/kernel/arch/x86/boot/bzImage。

与此相对应的映射文件将位于 ~/build/kernel/System.map。安装阶段需要这两个文件。

当内核行为异常并生成“Oops”消息时,System.map 文件很有用。“Oops”是在某些内核错误(由于内核错误或硬件故障)上生成的。

此错误类似于 Microsoft Windows 中的蓝屏死机 (BSOD)。这些消息包含有关系统当前状态的大量详细信息,包括几个十六进制数字。

System.map 允许 Linux 将这些十六进制数字转换为可读名称,从而简化调试。虽然这主要是为了开发人员的利益,但它在您报告问题时可能很方便。

让我们详细介绍一下安装新内核映像所需的步骤。

  1. 在内核构建目录的根目录中,将 bzImage 文件复制并重命名到 /boot 目录中

    sudo cp ~/build/kernel/arch/x86/boot/bzImage \
    /boot/vmlinuz-<kernel-version>
    

    这里,<kernel-version> 是内核的版本号。在本指南中使用的示例内核的文件名为 vmlinuz-6.5.7-custom。因此,本示例的精确命令为

    sudo cp ~/build/kernel/arch/x86/boot/bzImage \
    /boot/vmlinuz-6.5.7-custom
    

    注意

    将内核映像命名为 vmlinuz-6.5.7-custom 的决定在某种程度上是任意的。这很方便,因为内核映像通常被称为 vmlinuz,而版本号的后缀在您拥有多个内核或提供特定功能的内核时很有用(例如,vmlinuz-6.50.0-ws)。

  2. 现在内核映像已到位,请使用相同的命名约定将相应的 System.map 文件复制并重命名到 /boot 目录中

    sudo cp -v  ~/build/kernel/System.map \
    /boot/System.map-6.5.7-custom
    
  3. 内核已到位,System.map 文件已到位,模块也已到位,我们现在已准备好进行最后一步。所需命令的语法为

    kernel-install add <kernel-version> <kernel-image>
    

    这里,<kernel-version> 是内核的版本号(和名称)。<kernel-image> 是新编译的内核映像的路径。

    对于我们的示例,键入

    sudo kernel-install \
    add  6.5.7-custom /boot/vmlinuz-6.5.7-custom
    

kernel-install 命令是一个巧妙的 shell 脚本。它可能并非所有 Linux 发行版都提供,但它在较新的 Fedora、RHEL、CentOS 发行版中提供。此工具自动化了我们通常必须手动执行的许多最终操作,以设置系统以启动我们刚构建的新内核。

具体来说,该工具执行以下操作

  • 它创建适当的初始 RAM 文件系统映像(initramfs 映像,即 /boot/initramfs-<kernel-version>.img 文件)。要在没有 kernel-install 的系统上手动执行此操作,请使用 mkinitramfs 命令。
  • 它运行 depmod 命令(它创建模块依赖项列表)。
  • 它更新引导加载程序配置。

对于运行较新版本 GRUB2 的系统,该文件将为 /boot/grub2/grub.cfg。对于基于 EFI 的系统,/boot/efi/<distro>/fedora/grub.cfg 也会更新。

对于运行旧版本 GRUB 的系统,这将是 /boot/grub/grub.conf 或 /boot/grub/menu.lst 文件。对于已实施新引导加载程序规范 (BLS) 的最新发行版,将向 /boot/loader/entries/ 目录或 blsdir 变量指向的任何目录添加新的引导加载程序条目。

在我们的演示 EFI 基于 Rocky 服务器上,运行使用 BLS 的 GRUB 2,这里创建了一个新的引导条目:/boot/loader/entries/6fa25ca775f64accb0d3e53f0e4e6e92-6.5.7-custom.conf

$ sudo cat  /boot/loader/entries/6fa25ca775f64accb0d3e53f0e4e6e92-6.5.7-custom.conf
title Rocky Linux (6.5.7-custom) 8.5 (Green Obsidian)
version 6.5.7-custom
linux /vmlinuz-6.5.7-custom
initrd /initramfs-6.5.7-custom.img $tuned_initrd
options $kernelopts $tuned_params
id rocky-20220212013135-6.5.7-custom
grub_users $grub_users
grub_arg --unrestricted
grub_class kernel

注意

大多数发行版都有几个 grub2-* 实用程序可以使用,这些实用程序可以用来执行各种 GRUB2 和引导加载程序管理任务。例如,您可以使用 grub2-set-default 命令更改或设置在系统启动时启动的默认内核。

启动自定义内核

下一阶段是测试内核以确保系统可以启动它。

  1. 假设您完全按照医生的指示完成了所有操作,并且一切按医生的指示顺利完成,那么您可以安全地重新启动系统,并在系统启动时从引导加载程序菜单中选择新的内核。

    sudo reboot
    
  2. 系统启动后,您可以使用 uname 命令找出当前内核的名称

    $ uname -r
    6.5.7-custom
    
  3. 您会记得,我们添加到新内核的一个功能是能够支持 NTFS 文件系统。请确保新内核确实支持 NTFS,方法是显示有关 NTFS 模块的信息

    [rockstar ~]$ modinfo ntfs
    filename:       /lib/modules/6.5.7-custom/kernel/fs/ntfs/ntfs.ko
    license:        GPL
    version:        2.1.32
    description:    NTFS 1.2/3.x driver - Copyright …..
    ...OUTPUT TRUNCATED...
    

就是这样!

作者:Wale Soyinka

贡献者:Steven Spencer、Louis Abel、Ganna Zhyrnova