Linux版本说明

  1. linux-next 树: 所有准备合并到 Linux 代码库的代码首先被合并到 linux-next 树。它代表的是 Linux 内核最新也是“最不稳定”的状态。大多数 Linux 内核开发者和测试人员使用这个来提高代码质量,为 Linus Torvalds 的后续提取做准备。请谨慎使用!
  2. 发布候选版(RC) / 主线版: Linus 从 linux-next 树抽取代码并创建一个初始发布版本。这个初始发布版本的测试版称为 RC(发布候选Release Candidate)版本。一旦 RC 版本发布,Linus 只会接受对它的错误修复和性能退化相关的补丁。基础这些反馈,Linus 会每周发布一个 RC 内核,直到他对代码感到满意。RC 发行版本的标识是 -rc 后缀,后面跟一个数字。
  3. 稳定版: 当 Linus 觉得最新的 RC 版本已稳定时,他会发布最终的“公开”版本。稳定发布版将会维护几周时间。像 Arch Linux 和 Fedora Linux 这样的前沿 Linux 发行版会使用此类版本。我建议你在试用 linux-next 或任何 RC 版本之前,先试一试此版本。
  4. LTS 版本: 每年最后一个稳定版将会再维护 几年。这通常是一个较旧的版本,但它会 会积极地维护并提供安全修复。Debian 的稳定版本会使用 Linux 内核的 LTS 版版本。

对于 Arch Linux 以及其衍生版本的用户,安装命令如下:

1
sudo pacman -S base-devel bc coreutils cpio gettext initramfs kmod libelf ncurses pahole perl python rsync tar xz

对于 Debian 以及其衍生版本的用户,安装命令如下:

1
sudo apt install bc binutils bison dwarves flex gcc git gnupg2 gzip libelf-dev libncurses5-dev libssl-dev make openssl pahole perl-base rsync tar xz-utils

对于 Fedora 以及其衍生版本的用户,安装命令如下:

1
sudo dnf install binutils ncurses-devel \    /usr/include/{libelf.h,openssl/pkcs7.h} \    /usr/bin/{bc,bison,flex,gcc,git,gpg2,gzip,make,openssl,pahole,perl,rsync,tar,xz,zstd}

[Linux Kernel Archives](The Linux Kernel Archives)

为了验证 Tar 文件的完整性,需要先解压它。目前,它是使用 XZ 压缩算法压缩的。使用 unxz 工具(其实就是 xz --decompress 的别名)来解压 .tar.xz 格式的压缩文件。

1
unxz --keep linux-*.tar.xz

指定内核配置

1
cp /boot/config-6.1.0-28-amd64 ~/kernel/linux-source-6.12.8/.config

若当前内核配置文件没有在/boot文件夹内或者/boot文件夹内未找到内核配置文件,可使用如下命令:

1
sudo zcat /proc/config.gz > /path/to/.config

说明:
其中的/path/to/为下载的内核源码所在的文件夹;
若已进入下载的内核源码所在的文件夹仅输入.config即可,即:
sudo cp /boot/config-$(uname -r) .config

1
sudo zcat /proc/config.gz > .config

学习内核开发的目的学习如何构建 Linux 内核,那你应该这样做。

请注意,偏离你的 Linux 发行版的配置可能无法在实体硬件上“正常”工作。问题可能是特定硬件无法工作、Linux 内核无法启动等。

因此,只建议在虚拟机中使用。

可以通过查看 make help 的输出 来查看 所有 可用的选项,但我们主要关注三个 make 目标:

defconfig: 默认配置。
allmodconfig: 根据当前系统状态,尽可能地把项目构建为可加载模块(而非内建)。
tinyconfig: 极简的 Linux 内核。
由于 tinyconfig 目标只会构建少数项目,构建时间将会缩短。我个人选择它的原因主要有:

检查我在代码/工具链中做的修改是否正确,以及代码是否可以编译。
在虚拟机中只进行少数选项的测试。
在为 ARM 或 RISC-V 机器构建 Linux 内核时,你可能需要 DTB(设备树的二进制文件)。使用 tinyconfig 目标将不会启用构建 DTB 的选项,你的内核很可能无法启动。

可以用 QEMU 在没有任何 DTB 的情况下启动 Linux 内核。

除非你确切地知道自己在做什么,否则你应当使用 defconfig 目标。

修改配置

1
2
3
make menuconfig        # 文本界面配置工具 需要libncurses
make nconfig # 命令行配置工具
make xconfig # 图形界面配置工具 需要Qt库

目标架构默认(X86_64) 启动menuconfig

1
2
make ACH=目标架构 CROSS_COMPILE=工具链前缀 defconfig
make ARCH=目标架构 menuconfig

处理器类型与功能设置

1
Processor typer and features   # X86 AMD Intel    ARM SoC Cortex-A

平台设备支持

1
Device Drivers              # ARm 可能需要GPIO、SPI、I2C

设备树支持(嵌入式系统)

1
Boot options --> Use a device tree to describe hardware

虚拟化和模拟支持

1
Virtualization          # QEMu模拟ARM64

生成设备树文件(ARM和ARM64特有)

1
Device Drivers  --->  OF(Open Firmware) device tree support

检查配置架构

1
cat .config | grep CONFIG_ARCH

X86_64

1
cat .config | grep CONFIG_X86_64

ARM

1
cat .config | grep CONFIG_ARM

添加自定义内核构建标签

1
2
3
./scripts/config --file .config --set-str LOCALVERSION "-custom"
# 或更改
.config配置文件下的 **CONFIG_DEFAULT_INIT="-custom"** # linux内核生成为 `6.12.8-custom`

修改内核显示CC version ,定位Makefile文件中CC_VERSION_TEXT

1
2
3
4
CC_VERSION_TEXT = $(subst $(pound),,$(shell LC_ALL=C $(CC) --version 2>/dev/null | head -n 1))
修改为
CC_VERSION_TEXT = $(patsubst %,% - Custom Compiler Info
,$(filter clang gcc,$(shell LC_ALL=C $(CC) --version 2>/dev/null | head -n 1)))

编译

编译内核

1
2
make -j$(nproc)
#make -j$(nproc) 2>&1 | tee log

其中的 -j 选项用于指定并行编译任务的数量。而 nproc 命令用于返回可用处理单位(包括线程)的数量。因此,-j$(nproc) 其实意味着“使用我拥有的 CPU 线程数相同数量的并行编译任务”。

2>&1 会将 STDOUT 和 STDIN 重定向到相同的文件描述符,并通过管道传输给 tee 命令,这会将输出存储在一个名为 log 的文件,并且在控制台打印出完全相同的文本。如果你在构建时遇到错误,并希望回顾日志来检查出了什么问题,这将会十分有用。遇到那种情况,你只需要简单执行 grep Error log 命令就能找到线索。

1
make -j$(nproc) 2>&1 | tee log

这对大部分(99%)用户来说已经足够了。

其中的 -j 选项用于指定并行编译任务的数量。而 nproc 命令用于返回可用处理单位(包括线程)的数量。因此,-j$(nproc) 其实意味着“使用我拥有的 CPU 线程数相同数量的并行编译任务”。

2>&1 会将 STDOUT 和 STDIN 重定向到相同的文件描述符,并通过管道传输给 tee 命令,这会将输出存储在一个名为 log 的文件,并且在控制台打印出完全相同的文本。如果你在构建时遇到错误,并希望回顾日志来检查出了什么问题,这将会十分有用。遇到那种情况,你只需要简单执行 grep Error log 命令就能找到线索。

自定义 make 目标

构建 Linux 内核,或者只想构建模块,或者只想构建设备树二进制(DTB)。在这种情况下,你可以指定一个构建目标,然后 make 命令只会构建指定的项目,而不会构建其他的。

以下是一些构建目标:

  • vmlinux:纯粹的 Linux 内核。
  • modules:可加载模块。
  • dtbs:设备树二进制文件(主要用于 ARM 和 RISC-V 架构)。
  • all:构建所有被标记了星号 * 的项目(从 make help 的输出中可以查看)。

通常情况下,你并不需要指定构建目标,因为它们都已经在构建列表中。所列出的目标是在你只想要测试某一个构建目标,而不是其他目标时的情况。

依据你的 计算机架构,构建完成的 Linux 内核镜像(存放在 /boot 目录)的名称会有所不同。

对于 x86_64,Linux 内核的默认镜像名称是 bzImage。因此,如果你只需要构建引导所需的 Linux 内核,你可以像下面这样设定 bzImage 为目标:

1
2
# 对于 x86_64
make bzImage

“那么如何在我的架构上找到用来调用 make 的目标名称呢?”

有两种方法。要么你可以执行 make help 之后查找在 Architecture specific targets 下,第一个前面带有星号 * 的选项。

或者,如果你希望自动完成,你可以利用 image_name 目标得到镜像的完全路径(相对路径),选择性地添加 -s 标志来获得有用的输出。

以下是我拥有的三台电脑的输出,一台是 x86_64,另一台是 AArch64,还有一台是 riscv :

1
2
3
4
# x86_64
make -s image_namearch/x86/boot/bzImage### AArch64$ make -s image_namearch/arm64/boot/Image.gz
# RISC-V
make -s image_namearch/riscv/boot/Image.gz

现在,要只构建 Linux 内核镜像,你可以这样进行:

1
make $(make -s image_name | awk -F '/' '{print $4}')

编译模块

1
make modules -j$(nproc)

安装

1
2
3
4
5
6
# 安装模块
sudo make modules_install # 将编译生成内核模块复制到/lib/modules/内核版本
# 安装内核
sudo make install # 将bzImage(内核镜像)复制到/boot命名为vmlinuz-内核版本 符号表文件System.map、配置文件.config 复制/boot
# 安装内核头文件
sudo make headers_install # 安装到 /usr 目录。同时还会在 /usr 目录内创建子目录 include/linux,然后将头文件安装到 /usr/include/linux

通过设定 INSTALL_HDR_PATH 变量,你可以修改 Linux 内核头文件的安装路径。

Arch-linux

如果你尝试执行了 make install 命令,可能已经注意到产生了错误。错误如下:

1
$ sudo make install    INSTALL /bootCannot find LILO.

要在 Arch Linux 上实际完成 Linux 内核的安装,我们需要手动复制 Linux 内核镜像文件。别担心,如果你使用的是 Arch Linux,手动操作应该是家常便饭了。

可以使用以下命令完成这个步骤:

1
sudo install -Dm644 "$(make -s image_name)" /boot/vmlinuz-<kernel_release>-<localversion>

因为我编译的是 6.5.5 版本的内核,所以我将会执行下面这条命令,你可以根据你的实际情况进行适当调整:

1
sudo install -Dm644 "$(make -s image_name)" /boot/vmlinuz-6.5.5-pratham

虽然不是必须的,但最好复制一份名为 System.map 和 .config

1
sudo cp -vf System.map /boot/System.map-<kernel_release>-<localversion>sudo cp -vf .config /boot/config-<kernel_release>-<localversion>

ARM架构

安装 DTB(只针对 ARM 和 RISC-V)如果你使用的是 x86_64 架构,那么你可以跳过此步骤!

如果你针对 ARM 或者 RISC-V 构建了内核,那么在运行 make 的过程中,设备树的二进制文件可能已经被编译出来了。你可以通过在 arch/<machine_architecture>/boot/dts 目录查找 .dtb 文件来确认这一点。

这里提供了一个快速检查的技巧:

1
2
3
4
5
6
### 对于 AArch32
$ find arch/arm/boot/dts -name "*.dtb" -type f | head -n 1 > /dev/null && echo "DTBs for ARM32 were built"
### 对于 AArch64
$ find arch/arm64/boot/dts -name "*.dtb" -type f | head -n 1 > /dev/null && echo "DTBs for ARM64 were built"
### 对于 RISC-V
$ find arch/riscv/boot/dts -name "*.dtb" -type f | head -n 1 > /dev/null && echo "DTBs for RISC-V were built"

如果你看到出现 DTBs for <arch> were built 的消息,那么你可以开始安装 DTB。这可以通过 dtbs_install 目标来实现。

需要使用 sudo,因为它们会被安装在 /boot/dtb-<kernel_release>-<localversion> 中,而这个目录是由 root 所拥有的。

1
sudo make dtbs_install

给开发者的提示: 就像安装模块一样,你可以使用 INSTALL_DTBS_PATH 变量指定一个自定义的路径来安装设备树二进制文件。

内核文件编译保存

内核映像文件:bzImage或zImage

符号表文件:System.map

配置文件:.config

模块文件:/lib/modules/<kernel-version>

1
2
3
4
5
6
7
8
9
# 打包关键文件
tar -cvf linux-kernel-version.tar arch/x86/boot/bzImage System.map .config /lib/modules/<kernel-version>

# 安装
tar -xvf linux-kernel-version.tar
cp arch/x86/boot/bzImage /boot/vmlinuz-<kernel-version>
cp System.map /boot/System.map-<kernel-version>
cp .config /boot/config-<kernel-version>
update-grub # grub-mkconfig -o /boot/grub/grub.cfg

更新引导

1
2
3
4
5
6
7
8
9
10
sudo update-grub              # 没有update-grub 可以
sudo grub-mkconfig -o /boot/grub/grub.cfg
# 设置默认启动内核
sudo grubby --set-default /boot/vmlinuz-********
# 查看是否修改成功:
sudo grubby --default-kernel
sudo grubby --default-index
sudo grubby --info=ALL
### 移除指定内核
sudo grubby --remove-kernel=/boot/vmlinuz-6.*.*

生成初始化RAM磁盘

当你安装 Arch Linux 时,可能已经了解过 mkinitcpio 这个工具。创建初始的 RAM 磁盘。

首先,创建一个预设文件。向 /etc/mkinitcpio.d/linux-<localversion>.preset 文件中添加以下内容,根据实际需要来替换 <kernel_release> 和 <localversion>。

1
ALL_config="/etc/mkinitcpio.conf"ALL_kver="/boot/vmlinuz-<kernel_release>-<localversion>"PRESETS=('default' 'fallback')default_image="/boot/initramfs-<kernel_release>-<localversion>.img"fallback_options="-S autodetect"

配置完成后,执行下面的命令来生成初始 RAM 磁盘:

arch

1
sudo mkinitcpio -p linux-<localversion>

debian fedora

1
2
sudo dracut
# sudo dracut --force

查看模块和内核

1
2
3
4
# 内核
ls /lib/modules/内核版本
# 模块
lsmod

清除编译文件

1
2
3
make clean     # 清除编译产生文件,保留.config配置
make mrproper # 清除.config和生成模块
make distclean # 清除编译、配置、补丁文件
  • clean:除了 .config 文件外,删除几乎所有其他内容。
  • mrproper:执行了 make clean 的所有操作外,还会删除 .config 文件。
  • distclean:除了执行 make mrproper 的所有操作外,还会清理任何补丁文件。

卸载

1
2
3
4
5
6
# 删除内核模块
rm -rf /lib/modules/<kernel_release>-<localversion>
# 删除设备树二进制文件
rm -rf /boot/dtb-<kernel_release>-<localversion>
# 删除 Linux 内核本身
rm -vf /boot/{config,System,vmlinuz}-<kernel_release>-<localversion>