Linux 软件包包含 Linux 内核。
构建内核需要三步 —— 配置、编译、安装。阅读内核源代码树中的 README
文件,了解不同于本手册的内核配置方法。
初次构建 Linux 内核是 LFS 构建过程中最具挑战性的环节之一。内核配置依赖于系统硬件和您的个人需求。内核配置包含大约 12,000 个选项,尽管只有约三分之一对于大多数计算机系统是必要的。LFS 编辑建议不熟悉内核编译的用户严格遵循以下步骤。这些步骤的目的是使您能够在第 11.3 节 “重启系统”中重启进入 LFS 系统,并通过命令行登录。这里给出的步骤并不试图优化或定制内核配置。
https://www.linuxfromscratch.org/hints/downloads/files/kernel-configuration.txt 介绍了内核配置的常识。https://anduin.linuxfromscratch.org/LFS/kernel-nutshell/ 提供了更多关于内核配置的信息。这两份文档都有些过时,但仍然较好地概括了配置过程。
如果所有其他尝试都无法解决问题,可以在 lfs-support 邮件列表提问。注意为了防止垃圾邮件,必须先订阅列表才能向列表发送邮件。
运行以下命令,准备编译内核:
make mrproper
该命令确保内核源代码树绝对干净,内核开发组建议在每次编译内核前运行该命令。尽管内核源代码树在解压后应该是干净的,但这并不完全可靠。
有多种配置内核选项的方法。例如,通常我们通过目录驱动的界面完成这一工作:
make menuconfig
以上命令中可选的 make 环境变量及含义:
LANG=<host_LANG_value>
LC_ALL=
它们根据宿主使用的 locale 建立 locale 设定。在 UTF-8 Linux 文本终端下,有时必须这样做才能正确绘制基于 ncurses 的配置菜单接口。
在这种情况下,一定要将 <host_LANG_value>
替换成宿主环境中的 $LANG
变量值。您也可以使用宿主环境中
$LC_ALL
或 $LC_CTYPE
的值代替。
这会启动 ncurses 目录驱动的界面。如果希望了解其他 (图形) 界面,可以输入 make help。
一个较好的初始内核配置可以通过运行 make defconfig 获得。它会考虑您的当前系统体系结构,将基本内核配置设定到较好的状态。
一定要按照以下列表启用/禁用/设定其中列出的内核特性,否则系统可能不能正常工作,甚至根本无法引导:
General setup ---> [ ] Compile the kernel with warnings as errors [WERROR] CPU/Task time and stats accounting ---> [*] Pressure stall information tracking [PSI] [ ] Require boot parameter to enable pressure stall information tracking ... [PSI_DEFAULT_DISABLED] < > Enable kernel headers through /sys/kernel/kheaders.tar.xz [IKHEADERS] [*] Control Group support ---> [CGROUPS] [*] Memory controller [MEMCG] [ ] Configure standard kernel features (expert users) ---> [EXPERT] Kernel type and options ---> -*- EFI runtime service support [EFI] [*] EFI boot stub support [EFI_STUB] [*] Relocatable kernel [RELOCATABLE] [*] Randomize the address of the kernel (KASLR) [RANDOMIZE_BASE] General architecture-dependent options ---> [*] Stack Protector buffer overflow detection [STACKPROTECTOR] [*] Strong Stack Protector [STACKPROTECTOR_STRONG] Device Drivers ---> Generic Driver Options ---> [ ] Support for uevent helper [UEVENT_HELPER] [*] Maintain a devtmpfs filesystem to mount at /dev [DEVTMPFS] [*] Automount devtmpfs at /dev, after the kernel mounted the rootfs ... [DEVTMPFS_MOUNT] Firmware Drivers ---> [*] Mark VGA/VBE/EFI FB as generic system framebuffer [SYSFB_SIMPLEFB] EFI (Extensible Firmware Interface) Support ---> [*] Enable the generic EFI decompressor [EFI_ZBOOT] Graphics support ---> <*> Direct Rendering Manager (XFree86 4.1.0 and higher DRI support) ---> ... [DRM] [*] Display a user-friendly message when a kernel panic occurs ... [DRM_PANIC] (kmsg) Panic screen formatter [DRM_PANIC_SCREEN] Supported DRM clients ---> [*] Enable legacy fbdev support for your modesetting driver ... [DRM_FBDEV_EMULATION] Drivers for system framebuffers ---> <*> Simple framebuffer driver [DRM_SIMPLEDRM] Console display driver support ---> [*] Framebuffer Console support [FRAMEBUFFER_CONSOLE]
如果 LFS 系统分区在 NVME SSD 上 (即,分区的设备节点是 /dev/nvme*
,而非 /dev/sd*
),启用 NVME 支持,否则 LFS 系统无法引导:
Device Drivers ---> NVME Support ---> <*> NVM Express block device [BLK_DEV_NVME]
根据系统的需求,可能需要一些其他配置选项。BLFS 软件包需要的内核配置选项列表可以在 BLFS 内核配置索引查阅。
上述配置选项的含义:
Randomize the
address of the kernel image (KASLR)
为内核映像启用 ASLR,以预防一些基于内核中关键数据或代码的固定地址的攻击。
Compile the
kernel with warnings as errors
如果使用了和内核开发者不同的编译器和/或配置,启用该选项可能导致构建失败。
Enable kernel
headers through /sys/kernel/kheaders.tar.xz
启用该选项将会导致构建内核需要 cpio。LFS 没有安装 cpio。
Configure
standard kernel features (expert users)
该选项会导致配置界面出现一些新选项,但改变这些选项的设定值可能导致危险后果。不要使用该选项,除非您知道您在做什么。
Strong Stack
Protector
为内核启用 SSP。我们通过在配置 GCC 时使用 --enable-default-ssp
,已经为所有用户态代码启用了它,但是内核并不使用
GCC 默认的 SSP 设定。因此我们在这里显式地启用它。
Support for
uevent helper
如果启用了该选项,它可能干扰 Udev 的设备管理。
Maintain a
devtmpfs
该选项会使内核自动创建设备节点,即使 Udev 没有运行。Udev 之后才在这些设备节点的基础上运行,管理它们的访问权限并为它们建立符号链接。所有 Udev 用户都需要启用该选项。
Automount
devtmpfs at /dev
该选项使得内核在切换到根文件系统之后,执行 init 前,将内核获知的设备信息挂载到 /dev。
Enable the
generic EFI decompressor
将可引导映像构建为携带压缩过的实际内核映像的 EFI 应用程序。这可以使可引导映像减小 50%。
Display a
user-friendly message when a kernel panic
occurs
该选项使得内核在内核恐慌时正确显示消息。如果不启用它,诊断内核恐慌原因会更加困难:如果没有运行 DRM
驱动,则我们只能使用 VGA 控制台,而它只能显示 24 行,因此有用的内核消息往往已被刷新到屏幕以外;而如果正在运行
DRM 驱动,在内核恐慌时屏幕显示往往会完全错乱。在 Linux-6.12 中,适用于主流 GPU
型号的驱动程序都无法真正支持该选项提供的功能,但在专用的 GPU 驱动加在之前,EFI 帧缓冲之上运行的简单帧缓冲驱动
(“Simple framebuffer
driver”) 能够支持它。如果将专用的 GPU 驱动构建为内核模块
(而非内核映像的一部分),且没有使用
initramfs,则这一功能在根文件系统成功挂载之前会正常工作,而这足以提供诊断大多数由错误配置 LFS
(例如,第 10.4 节 “使用 GRUB
设定引导过程” 中 root=
的设定值不正确) 导致的内核恐慌。
Panic screen
formatter
将该选项设为 kmsg
,以确保在内核恐慌时显示恐慌前的内核消息。该选项的默认值,user
,会导致内核只显示一条“用户友好”的,对于诊断毫无帮助的消息。还有一个可选的值
qr_code
,会使得内核将恐慌前的消息压缩到一个二维码中,并显示该二维码。和文本输出相比,使用外部设备
(如智能手机) 解码二维码能得到更多行内核消息。但二维码输出功能需要 Rust 编译器才能构建,而 LFS 不提供
Rust 编译器。
Mark VGA/VBE/EFI
FB as generic system framebuffer
和 Simple framebuffer driver
它们允许使用 EFI 帧缓冲作为 DRM 设备。EFI 固件会设置好 EFI 帧缓冲,这样在 GPU 专用的 DRM 驱动加载前,基于 DRM 的内核恐慌处理程序也能正常工作。
Enable legacy
fbdev support for your modesetting driver
和
Framebuffer Console
support
它们允许使用基于 DRI (Direct Rendering Infrastructure) 的 GPU 驱动显示
Linux 控制台。鉴于已经启用了 CONFIG_DRM
(Direct Rendering Manager),需要同时启用这两项特性,否则一旦 DRI
驱动被加载,就只能看到空白屏幕。
某些情况下,make oldconfig
更为合适。阅读 README
文件了解更多信息。
如果希望的话,也可以将宿主系统的内核配置文件 .config
拷贝到解压出的 linux-6.16.1
目录
(前提是可以找到该文件)。然而我们不推荐这样做,一般来说,浏览整个配置目录,并从头创建内核配置是更好的选择。
编译内核映像和模块:
make
如果要使用内核模块,可能需要在 /etc/modprobe.d
中写入模块配置。讨论模块和内核配置的信息位于第 9.3 节 “设备和模块管理概述”和
linux-6.16.1/Documentation
目录下的内核文档中。另外 modprobe.d(5)
也可以作为参考。
如果内核配置使用了模块,安装它们:
make modules_install
在内核编译完成后,需要进行额外步骤完成安装,一些文件需要拷贝到 /boot
目录中。
如果要使用单独的 /boot
分区 (包括和宿主发行版共用一个
/boot
分区的情况),需要将这些文件拷贝到该分区中。最简单的方法是首先在 /etc/fstab
中加入 /boot
分区的条目 (详见前一节),然后在 chroot 环境中以 root
身份执行以下命令:
mount /boot
该命令中省略了指向设备节点的路径,因为 mount 可以从 /etc/fstab
中读取它。
指向内核映像的路径可能随机器平台的不同而变化。下面使用的文件名可以依照您的需要改变,但文件名的开头应该保持为 vmlinuz,以保证和下一节描述的引导过程自动设定相兼容。下面的命令假定使用 LoongArch 平台,以及 EFI 引导加载器 (例如,在第 8 章中构建的 GRUB):
cp -iv arch/loongarch/boot/vmlinuz.efi /boot/vmlinuz-6.16.1-lfs-loongarch-r12.3-142
System.map
是内核符号文件,它将内核 API
的每个函数入口点和运行时数据结构映射到它们的地址。它被用于调查分析内核可能出现的问题。执行以下命令安装该文件:
cp -iv System.map /boot/System.map-6.16.1
内核配置文件 .config
由上述的 make menuconfig
步骤生成,包含编译好的内核的所有配置选项。最好能将它保留下来以供日后参考:
cp -iv .config /boot/config-6.16.1
安装 Linux 内核文档:
cp -r Documentation -T /usr/share/doc/linux-6.16.1
需要注意的是,在内核源代码目录中可能有不属于 root 的文件。在以 root 身份解压源代码包时 (就像我们在 chroot 环境中所做的那样),这些文件会获得它们之前在软件包创建者的计算机上的用户和组 ID。这一般不会造成问题,因为在安装后通常会删除源代码目录树。然而,Linux 源代码目录树一般会被保留较长时间,这样创建者当时使用的用户 ID 就可能被分配给本机的某个用户,导致该用户拥有内核源代码的写权限。
之后在 BLFS 中安装软件包时往往需要修改内核配置。因此,和其他软件包不同,我们在安装好内核后可以不移除源代码树。
如果要保留内核源代码树,对内核源代码目录运行 chown -R
0:0 命令,以保证 linux-6.16.1
目录中所有文件都属于 root。
如果正在使用保留的内核源码树和新的内核配置重新构建内核,则一般不>应该执行 make mrproper 命令。该命令会清理
.config
文件和之前构建的所有 .o
文件。尽管可以从 /boot
中的副本恢复 .config
,清理所有 .o
文件仍然会造成巨大的浪费:对于简单的内核配置变更,通常只需要 (重新) 构建少数几个
.o
文件,在不进行清理时,内核构建系统会正确跳过其他
.o
文件。
然而,如果更新了 GCC,则应该执行 make
clean 命令,以清理之前构建的所有 .o
文件,否则可能导致构建失败。
有的内核文档建议创建符号链接 /usr/src/linux
指向内核源代码目录,这仅仅适用于 2.6 系列之前的内核。在 LFS 系统上绝对不要创建它,因为在构建完基本 LFS
系统后,它可能在您构建其他软件包时引起问题。
多数情况下 Linux 内核模块可以自动加载,但有时需要指定加载顺序。负责加载内核模块的程序 modprobe 和 insmod 从 /etc/modprobe.d
下的配置文件中读取加载顺序,例如,如果 USB 驱动程序
(ehci_hcd、ohci_hcd 和 uhci_hcd) 被构建为模块,则必须按照先加载 echi_hcd,再加载
ohci_hcd 和 uhci_hcd 的正确顺序,才能避免引导时出现警告信息。
为此,执行以下命令创建文件 /etc/modprobe.d/usb.conf
:
install -v -m755 -d /etc/modprobe.d
cat > /etc/modprobe.d/usb.conf << "EOF"
# Begin /etc/modprobe.d/usb.conf
install ohci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i ohci_hcd ; true
install uhci_hcd /sbin/modprobe ehci_hcd ; /sbin/modprobe -i uhci_hcd ; true
# End /etc/modprobe.d/usb.conf
EOF