28.6 构建定制内核
内核是 FreeBSD 操作系统的核心,负责管理内存、执行安全控制、网络通信、磁盘访问等任务。虽然 FreeBSD 的大部分内容是动态可配置的,但一些用户可能希望配置并编译自定义内核。
28.6.1 为何构建定制内核
传统上,FreeBSD 使用的是宏内核。宏内核是一种大型程序,支持固定的设备列表,并且如果要改变内核的行为,必须编译并重新启动到新内核。
如今,FreeBSD 内核中的大部分功能都包含在可以根据需要动态加载和卸载的模块中。这使得运行时内核能够立即适应新硬件,故而将这种内核称为模块化内核。
有时,仍需对内核进行静态配置。某些功能与内核紧密绑定,无法动态加载。此外,一些安全环境会阻止加载和卸载内核模块,要求只能将所需功能静态编译进内核。
构建定制内核的过程虽然耗时,但有其实际价值。与默认的 GENERIC 内核不同(GENERIC 必须支持大量硬件),定制内核可以精简为仅支持本机硬件。以下为定制内核的主要优点:
- 缩短启动时间。定制内核仅探测本机硬件,可减少启动耗时。
- 降低内存占用。定制内核通过省略不使用的功能和设备驱动以减少内存占用。内核代码始终驻留在物理内存中,应用程序无法占用,因此定制内核对于内存较小的系统尤为重要。
- 增强的硬件支持。定制内核可以为 GENERIC 内核中没有的设备提供支持。
警告
非默认配置的测试不如 GENERIC 配置充分。虽然定制内核可以带来特定收益,但也增加了遇到构建或运行时问题的风险。仅建议有明确理由更改、且愿意在必要时调试的专业用户使用定制内核配置。
构建定制内核前,应明确其目的。具体而言,如果需要特定的硬件支持,它可能已经作为模块存在。
内核模块位于 /boot/kernel 中,可以使用 kldload(8) 将其动态加载到正在运行的内核。
/boot/kernel 目录中内容类似如下输出:
ykla@ykla:~ $ ls /boot/kernel
aac.ko iwn105fw.ko
ahci.ko lindebugfs.ko
……省略部分输出……
u3g.ko xdr.ko
iwi_ibss.ko xhci.ko
iwi_monitor.ko xz.ko
zfs.ko zlib.ko大多数内核驱动程序都有可加载的模块。
如果要在启动时作为模块加载驱动程序 u3g,可在 /boot/loader.conf 文件中加入以下行:
u3g_load="YES"系统将在启动时动态加载此模块。
然而,有些情况下,在 /boot/kernel 中没有关联的模块,这通常适用于特定的子系统。
28.6.2 准备工作
28.6.2.1 获取内核源代码
FreeBSD 操作系统是一个作为整体开发维护的系统,因此内核和用户空间都位于 freebsd-src 项目中。
为了创建自定义内核配置文件并构建自定义内核,必须首先安装完整的 FreeBSD 源代码树。
如果 /usr/src/ 目录不存在或为空,则说明源代码未安装。可以通过 Git 安装源代码或者在镜像站下载对应的 src 归档文件。
示例:通过 16.0-CURRENT 归档文件获得 FreeBSD 源代码,并解压至目录 /usr/src/。
$ fetch http://ftp.freebsd.org/pub/FreeBSD/snapshots/amd64/16.0-CURRENT/src.txz
# tar xvf src.txz -C /
# rm src.txz # 删除不再需要的源代码归档文件内核源代码路径为 /usr/src/sys。
28.6.3 创建内核配置文件
28.6.3.1 文件命名规则
安装完成后,请查看 /usr/src/sys 目录的内容:
# ls -a /usr/src/sys
. conf isa netlink rpc
.. contrib kern netpfil security
Makefile crypto kgssapi netsmb sys
README.md ddb libkern nfs teken
amd64 dev modules nfsclient tests
arm dts net nfsserver tools
arm64 fs net80211 nlm ufs
bsm gdb netgraph ofed vm
cam geom netinet opencrypto x86
cddl gnu netinet6 powerpc xdr
compat i386 netipsec riscv xen该目录包含多个子目录,其中包括表示受支持架构的目录(如 amd64、i386、arm、riscv、powerpc 等)。
# ls /usr/src/sys/amd64
Makefile conf linux pt
acpica ia32 linux32 sgx
amd64 include pci vmm每个特定架构的目录只处理该架构的相关内容,其他代码则是所有平台通用的机器无关代码。
# ls /usr/src/sys/amd64/conf/
DEFAULTS GENERIC-KCSAN GENERIC.hints LINT-NOIP NOTES
FIRECRACKER GENERIC-KMSAN LINT LINT-NOVIMAGE
GENERIC GENERIC-MMCCAM LINT-NOINET MINIMAL
GENERIC-KASAN GENERIC-NODEBUG LINT-NOINET6 MINIMAL-NODEBUG每个受支持架构都有 conf 子目录,其中包含该架构的 GENERIC 内核配置文件。
因此,不应直接编辑 GENERIC 文件,而应将文件复制并编辑其副本。配置文件名称必须 全部使用大写字母。在管理多台具有不同硬件的 FreeBSD 机器时,建议将其命名为机器的主机名。
注意
构建内核时,系统会在 /usr/obj/usr/src/amd64.amd64/sys/ 下创建与配置同名的目录存放构建产物。
28.6.3.2 编辑配置文件
以下示例是 amd64 架构的 16.0-CURRENT,为其 GENERIC 配置文件创建副本 MYKERNEL:
# cp /usr/src/sys/amd64/conf/GENERIC /usr/src/sys/amd64/conf/MYKERNEL现在可以使用文本编辑器自定义内核配置文件 MYKERNEL。
内核配置文件的格式简单。
警告
删除设备或选项的支持可能导致内核损坏。例如,如果从内核配置文件中删除 NVMe 驱动程序,使用
nvme磁盘驱动程序的系统可能无法启动。如有疑问,建议将支持保留在内核中。
配置文件中可以使用 include 指令,将另一个配置文件包含到当前配置文件中,便于在现有配置的基础上维护小的变更。如果只需要少量额外选项或驱动程序,可通过这种方法维护与 GENERIC 的差异。
cpu HAMMER
ident GENERIC
makeoptions DEBUG=-g # Build kernel with gdb(1) debug symbols
makeoptions WITH_CTF=1 # Run ctfconvert(1) for DTrace support
options SCHED_ULE # ULE scheduler
options SCHED_4BSD # Original 4.xBSD scheduler
options NUMA # Non-Uniform Memory Architecture support
options PREEMPTION # Enable kernel thread preemption
options EXTERR_STRINGS
options VIMAGE # Subsystem virtualization, e.g. VNET
# For full debugger support use (turn off in stable branch):
include "std.debug"
……省略其他选项……注意
使用
include指令时,其行为类似 C 语言的#include,会将目标文件的全部内容插入当前位置。以GENERIC为例,其首行为include "std.amd64",引入了标准的 amd64 基础配置。使用这种方法,本地配置文件表示与 GENERIC 内核的本地差异。在执行升级时,除非使用nooptions或nodevice明确防止,否则 GENERIC 中新增的特性也会添加到本地内核。
常用配置指令
| 指令 | 用途 |
|---|---|
machine | 指定目标体系结构 |
ident | 指定内核名称,便于识别 |
options | 启用手动配置的内核选项 |
nooptions | 禁用不需要的内核选项 |
device | 编译并包含设备驱动 |
nodevice | 移除特定设备驱动 |
makeoptions | 向 Makefile 传递构建参数 |
实际上,上述示例出自 16.0-CURRENT 的 GENERIC 内核配置文件,-CURRENT 与 -STABLE、-RELEASE 的重要差异就在于 -CURRENT 引入了大量的调试功能。该功能即由 include "std.debug" 引入,部分用户因而会体验到 -CURRENT 系统性能显著下降。
现在移除这些调试功能,以自定义一个标准配置的内核。编辑 /usr/src/sys/amd64/conf/MYKERNEL 文件,在 include "std.debug" 前添加注释符号,禁用这些调试功能。
#include "std.debug"技巧
还可以使用以下命令
sh# sed -i '' 's/^include "std.debug"/#include "std.debug"/' /usr/src/sys/amd64/conf/MYKERNEL实现上述替换。这在脚本文件中较为实用。
28.6.4 构建与安装
28.6.4.1 构建内核
保存自定义配置文件的编辑内容后,即可按以下步骤编译内核源代码。
编译内核需在 /usr/src 目录下执行,这取决于环境变量 SYSDIR 的设置(可覆盖 /usr/src/sys)。切换到此目录:
# cd /usr/src对于 pkgbase 系统:必须先构建完整的用户空间。将内核和用户空间构建为 repo 软件源后方可安装。使用以下命令构建用户空间:
# make -s -j$(sysctl -n hw.ncpu) buildworld KERNCONF=MYKERNEL通过指定自定义内核配置文件的名称以编译新内核:
# make -s -j$(sysctl -n hw.ncpu) buildkernel KERNCONF=MYKERNEL选项说明:
-s:静默模式,只输出若干警告、错误以及编译进度信息。-j:并行编译以缩短构建时间,建议数值为 CPU 核心数,sysctl -n hw.ncpu可自动识别该值。
上述命令输出如下:
make[1]: /usr/src/Makefile.inc1:371: SYSTEM_COMPILER: libclang will be built for bootstrapping a cross-compiler.
make[1]: /usr/src/Makefile.inc1:376: SYSTEM_LINKER: libclang will be built for bootstrapping a cross-linker.
--------------------------------------------------------------
>>> Kernel build for MYKERNEL started on Sat May 2 12:36:44 CST 2026
--------------------------------------------------------------
===> MYKERNEL
--------------------------------------------------------------
>>> stage 1: configuring the kernel
--------------------------------------------------------------
Kernel build directory is /usr/obj/usr/src/amd64.amd64/sys/MYKERNEL
Don't forget to do ``make cleandepend && make depend''
0.13 real 0.06 user 0.07 sys
--------------------------------------------------------------
>>> stage 2.3: build tools
--------------------------------------------------------------
0.16 real 0.06 user 0.11 sys
--------------------------------------------------------------
>>> stage 3.1: building everything
--------------------------------------------------------------
linking kernel.full
ctfmerge -L VERSION -g -o kernel.full ...
text data bss dec hex filename
21068157 1732469 8752832 31553458 1e177b2 kernel.full
986.52 real 7898.76 user 6298.50 sys
--------------------------------------------------------------
>>> Kernel build for MYKERNEL completed on Sat May 2 12:53:11 CST 2026
--------------------------------------------------------------
>>> Kernel(s) MYKERNEL built in 987 seconds, ncpu: 16, make -j16
--------------------------------------------------------------注意
首次构建可能需要较长时间(取决于硬件性能,通常为 10 到 60 分钟)。构建过程会在 /usr/obj/usr/src/amd64.amd64/sys/MYKERNEL/ 目录下生成产物。
在默认情况下,编译自定义内核时所有内核模块都会重新编译。要更快地更新内核或仅构建自定义模块,可以在开始构建内核之前编辑 /etc/make.conf 文件。
例如,以下变量指定要构建的模块列表,而非默认构建所有模块:
MODULES_OVERRIDE = linux acpi或者,此变量列出要从构建过程中排除的模块:
WITHOUT_MODULES = linux acpi sound28.6.4.2 在传统方式安装的系统上安装新内核
警告
安装新内核将替换
/boot/kernel/中的当前内核,旧内核移动至/boot/kernel.old/。若新内核配置有误导致无法启动,需在引导加载器中手动指定kernel.old启动。远程管理场景下,请确保具备带外管理或物理访问能力后再执行此操作。
安装与指定内核配置文件关联的新内核。新内核和内核模块安装至 /boot/kernel 目录,旧内核移动至 /boot/kernel.old/kernel,旧内核模块保留在 /boot/kernel.old 目录:
# make -s installkernel KERNCONF=MYKERNEL如果一切符合预期,则命令最后的输出应为如下行所示:
……省略其他输出……
--------------------------------------------------------------
>>> Installing kernel MYKERNEL completed on Sat May 2 13:21:43 CST 2026
--------------------------------------------------------------
>>> Install kernel(s) MYKERNEL completed in 9 seconds, ncpu: 16
--------------------------------------------------------------安装完成后需要重新启动以加载新内核:
# reboot28.6.4.3 在 pkgbase 系统上安装新内核
警告
目前无法仅构建内核。
构建内核的 pkgbase 软件包:
# make -s -j$(sysctl -n hw.ncpu) packages KERNCONF=MYKERNEL DISTDIR=/usr/obj/distpkgbase 构建结果将位于 /usr/obj/usr/src/repo/ 下,将其加入软件源或直接安装均可。
28.6.4.4 验证新内核
重启后通过以下命令验证是否成功加载了定制内核:
$ uname -a
FreeBSD ykla 16.0-CURRENT FreeBSD 16.0-CURRENT #0: Sat May 2 12:39:42 CST 2026 ykla@ykla:/usr/obj/usr/src/amd64.amd64/sys/MYKERNEL amd64uname -a 输出中应显示自定义内核名称 MYKERNEL。
$ sysctl kern.bootfile
kern.bootfile: /boot/kernel/kernel验证 CURRENT 的调试选项是否未启用,如 WITNESS:
$ dmesg | grep -i witness应无任何输出。
28.6.5 构建中的常见问题
28.6.5.1 内核启动失败(Kernel Panic)
若新内核无法启动,可在加载器提示符下选择引导旧内核。系统默认将上一次安装的内核保留在 /boot/kernel.old/ 中。
在 FreeBSD 引导菜单(beastie 菜单)中按数字键 6 进入加载器提示符(OK prompt),执行:
OK unload
OK load /boot/kernel.old/kernel
OK boot也可以指定直接引导旧内核:
OK boot /boot/kernel.old/kernel进入系统后,将旧内核恢复为默认:
警告
此操作将替换当前内核。若
kernel.old也有问题,系统将无法启动且无其他可回退的内核。建议在执行前先确认kernel.old内核可正常启动。
# mv /boot/kernel /boot/kernel.bad
# mv /boot/kernel.old /boot/kernel28.6.5.2 config 失败
如果 config 失败,它会打印出错误的行号。例如,对于以下消息:
config: line 17: syntax error请确保第 17 行写入的内容正确,可以与 GENERIC 或 NOTES 文件进行比较。
28.6.5.3 make 失败
如果 make 失败,通常是由于内核配置文件中的错误,但该错误尚不足以触发 config 的检测。请检查配置文件。
28.6.5.4 内核无法启动
如果新内核无法启动或无法识别设备,可在 FreeBSD 启动加载器中选择要启动的内核。在系统启动菜单出现时,选择“Escape to a loader prompt”选项。在提示符下,输入 boot kernel.old,或输入任何已知能正常启动的内核名称。
使用可用的内核启动后,请检查配置文件并重新构建。/var/log/messages 记录了每次成功启动的内核信息,dmesg 会打印当前启动的内核信息。
警告
替换内核操作不可逆。若
kernel.good也有问题,系统将无法启动且无其他可回退的内核。建议在执行前先确认回退内核可正常启动。
# mv /boot/kernel /boot/kernel.bad
# mv /boot/kernel.good /boot/kernel28.6.5.5 内核正常工作,但用户空间命令无法使用
如果内核版本与系统工具的构建版本不同,例如,对 -RELEASE 系统安装了从 -CURRENT 源代码构建的内核,许多系统状态命令(如 ps 和 vmstat)将无法正常运行。为此,须重新编译并安装与内核版本相同的用户空间。不建议使用与操作系统其他部分版本不同的内核。
28.6.5.6 构建错误:缺少依赖
若只安装了内核源代码而未安装完整源代码树,构建内核模块可能失败。可通过以下命令安装完整源代码:
# git clone --depth=1 https://github.com/freebsd/freebsd-src.git /usr/src28.6.5.7 编译中断后恢复
构建过程中断后,重新执行相同的 make buildkernel 命令即可。构建系统会自动检测已完成的部分并从中断处继续编译。
如果需要完全重新构建(例如修改了头文件),可先清理构建产物:
# make cleandir
# make buildkernel KERNCONF=MYKERNEL28.6.6 参考文献
- FreeBSD Project. build(7)[EB/OL]. [2026-04-30]. https://man.freebsd.org/cgi/man.cgi?query=build&sektion=7. FreeBSD 源代码构建系统手册页。