# 24.1 FreeBSD 内核文件结构

本章介绍 FreeBSD 操作系统内核的配置文件组织结构，从文件系统路径定位到具体配置指令语法，为内核定制和系统优化提供指导。内核配置是操作系统级定制的核心环节，通过调整配置参数可实现性能优化、硬件适配、功能裁剪等目标。

## 内核配置选项路径

FreeBSD 内核配置选项按照体系结构分别存放在不同的目录中，具体路径如下：

* amd64 架构的内核配置选项位于 [sys/amd64/conf](https://github.com/freebsd/freebsd-src/tree/main/sys/amd64/conf)
* arm64 架构的内核配置选项位于 [sys/arm64/conf](https://github.com/freebsd/freebsd-src/tree/main/sys/arm64/conf)
* RISC-V 架构的内核配置选项位于 [sys/riscv/conf](https://github.com/freebsd/freebsd-src/tree/main/sys/riscv/conf)

## 内核配置文件说明（基于 amd64）

以下介绍 [freebsd-src/main/sys/amd64/conf](https://github.com/freebsd/freebsd-src/tree/main/sys/amd64/conf) 目录下的主要内核配置文件：

* `DEFAULTS`：FreeBSD/amd64 的默认内核配置文件，作为 GENERIC 等配置的基础，只有不到 30 行
* `FIRECRACKER`：面向 Firecracker\[EB/OL]. \[2026-03-26]. <https://firecracker-microvm.github.io/> 虚拟机环境的内核配置文件，参见：amd64: Add FIRECRACKER kernel configuration\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D36672>
* `GENERIC`：FreeBSD/amd64 通用内核配置文件，所有 amd64 架构的系统镜像默认基于此配置构建，约 350 行，适用于大多数硬件和使用场景
* `GENERIC-KASAN`：调试开发用。用于内核地址清理器（Kernel Address Sanitizer，KASAN），参见：amd64: Add MD bits for KASAN\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D29455> 和 kasan(9)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=kasan&sektion=9&format=html>，不到十行
* `GENERIC-KCSAN`：调试开发用。内核并发清理器（Kernel Concurrency Sanitizer，KCSAN）。30 余行，参见：Port the NetBSD KCSAN runtime to FreeBSD\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D22315>
* `GENERIC-KMSAN`：调试开发用。不到十行。内核内存清理器（Kernel Memory SANitizer，KMSAN），参见：KMSAN(9)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=kmsan&sektion=9&format=html>
* `GENERIC-MMCCAM`：调试开发用。使用 MMCCAM 代替 MMC 栈。不到二十行。参见：MMC stack on top of CAM framework\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D4761>
* `GENERIC-NODEBUG`：该文件仅在 [main](https://github.com/freebsd/freebsd-src/blob/main) 分支中存在，用于基准测试。三十余行。基于 GENERIC 配置文件，但删除了 [WITNESS (4)](https://man.freebsd.org/cgi/man.cgi?witness\(4\))（跟踪每个线程获取和释放的锁）和 [INVARIANTS(9)](https://man.freebsd.org/cgi/man.cgi?KASSERT\(9\))（内核表达式验证宏）。CURRENT 的 GENERIC 默认启用了上述调试功能，通过引入 `std.debug` 实现，这会对系统性能产生影响，生产环境可考虑使用此配置
* `GENERIC.hints`：一些 [hints](https://man.freebsd.org/device.hints) 参数。二十余行。用于设置驱动的参数
* `LINT`：用于内核编译完整性检查的配置文件，参见：Eliminate building LINT makefiles\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D26540>，不到五行
* `LINT-NOINET`：LINT 文件，不到五行，禁用了 INET(4)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=inet&sektion=4&man>（IPv4）。参见：Eliminate building LINT makefiles\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D26540>
* `LINT-NOINET6`：LINT 文件，不到五行，禁用了 INET(6)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=inet6&sektion=4&man>（IPv6）。参见：Eliminate building LINT makefiles\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D26540>
* `LINT-NOIP`：LINT 文件，不到三十行，禁用了 IP 协议相关功能（IPv4、IPv6、TLS 等）。参见：Eliminate building LINT makefiles\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D26540>
* `LINT-NOVIMAGE`：调试开发用。LINT 文件，不到五行，禁用了 VIMAGE (9)\[EB/OL]. \[2026-03-26]. <https://man.freebsd.org/cgi/man.cgi?query=vimage&sektion=9>（实际上是 VNET(9) 的别名，网络子系统虚拟化基础设施）。参见：sys: add LINT-NOVIMAGE\[EB/OL]. \[2026-03-26]. <https://reviews.freebsd.org/D50780>
* `MINIMAL`：FreeBSD/amd64 中最常用的最简内核配置选项。160 余行，适用于资源受限的环境
* `MINIMAL-NODEBUG`：功能等同于 GENERIC-NODEBUG，但基于 `MINIMAL` 配置。不到 15 行

## 内核选项说明文件

除了内核配置文件外，还有相应的说明文件用于解释各种内核选项。这些说明文件分布在不同体系结构和通用配置目录下：

* [sys/amd64/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/amd64/conf/NOTES)：amd64 机器相关的内核配置说明文件，不到 200 行
* [sys/x86/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/x86/conf/NOTES)：i386 和 amd64 共用的机器相关配置，650 余行
* [sys/arm64/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/arm64/conf/NOTES)：arm64（aarch64）机器相关的内核配置说明文件，不到 270 行
* [sys/arm/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/arm/conf/NOTES)：armv7/32 位 arm 机器相关的内核配置说明文件，不到 100 行
* [sys/riscv/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/riscv/conf/NOTES)：riscv64（64 位 RISC-V）机器相关的内核配置说明文件，100 行
* [sys/powerpc/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/powerpc/conf/NOTES)：powerpc/powerpc64/powerpc64le 机器相关的内核配置说明文件，不到 100 行
* [sys/conf/NOTES](https://github.com/freebsd/freebsd-src/blob/main/sys/conf/NOTES)：机器无关的说明，近 2900 行

目录结构：

```sh
sys/
├── amd64/
│   └── conf/
│       ├── DEFAULTS
│       ├── FIRECRACKER
│       ├── GENERIC
│       ├── GENERIC-KASAN
│       ├── GENERIC-KCSAN
│       ├── GENERIC-KMSAN
│       ├── GENERIC-MMCCAM
│       ├── GENERIC-NODEBUG
│       ├── GENERIC.hints
│       ├── LINT
│       ├── LINT-NOINET
│       ├── LINT-NOINET6
│       ├── LINT-NOIP
│       ├── LINT-NOVIMAGE
│       ├── MINIMAL
│       ├── MINIMAL-NODEBUG
│       └── NOTES
├── x86/
│   └── conf/
│       └── NOTES
├── arm64/
│   └── conf/
│       └── NOTES
├── arm/
│   └── conf/
│       └── NOTES
├── riscv/
│   └── conf/
│       └── NOTES
├── powerpc/
│   └── conf/
│       └── NOTES
└── conf/
    ├── NOTES
    └── std.debug
```

## CURRENT 中的调试功能

FreeBSD-CURRENT（main 分支）作为开发主线，其设计目标之一是提供强大的调试基础设施，以支持内核和驱动程序的开发与测试。所有 main 主线上的 FreeBSD 系统（即 CURRENT）都启用了调试功能，这对调试开发系统极其有用。需要注意的是，这会对系统性能造成明显影响。

在 CURRENT 中的默认内核 GENERIC 均启用了 `include "std.debug"` 文件所述的调试功能（arm64 在文件 [sys/arm64/conf/std.arm64](https://github.com/freebsd/freebsd-src/blob/main/sys/arm64/conf/std.arm64) 中指定，由 GENERIC 间接引用）。

`std.debug` 文件由 [sys/amd64/conf: unify MINIMAL and GENERIC debug](https://github.com/freebsd/freebsd-src/pull/1124) 引入。

`std.debug` 文件的实际文本内容位于 [sys/conf/std.debug](https://github.com/freebsd/freebsd-src/blob/main/sys/conf/std.debug)。写作本文时，`std.debug` 文件约 20 行。

若需禁用调试功能以获得更佳性能，可使用 GENERIC-NODEBUG 配置，或创建自定义配置文件并移除 `std.debug` 的引用。

## 附录：man [config(5)](https://man.freebsd.org/cgi/man.cgi?config\(5\)) 中文

以下是对 config(5) 手册页的中文翻译整理，该页面详细介绍了内核配置文件的格式与用法：

### 名称

config - 内核配置文件格式

### 描述

内核配置文件用于指定 FreeBSD 内核的配置。它由 config(8) 处理，用于创建可使用 make(1) 构建内核的构建环境。

### 词法结构

内核配置文件由一系列规范指令组成，其词法结构规则如下：

规范指令以行首的关键字开始，后跟附加参数。

规范指令可以用分号 `;` 或换行符终止。长输入行可以通过以空白字符开始第二行及后续行来拆分成较短的行。

该语法大小写敏感，`machine` 和 `MACHINE` 是不同的标记。

双引号字符 `"` 开始一个带引号的字符串。直到下一个引号字符的所有字符构成带引号字符串的值。可以通过使用序列 `\"` 将 `"` 字符插入到带引号的字符串中。

数字使用 C 风格语法指定。

`#` 字符开始注释；从 `#` 字符到当前行末尾的所有字符都将被忽略。

标记之间的空白字符将被忽略，但在带引号的字符串内除外。注释行后的空白字符将被忽略。

### 配置指令

内核配置指令可以以任意顺序出现在内核配置文件中。指令按出现顺序处理，后续指令行会覆盖先前指令的效果。

关键字及其含义列表如下：

### `cpu cputype`

指定内核将在哪种 CPU 上运行。一个配置文件中可以有多个 cpu 指令。允许的 CPU 名称列表是体系结构特定的，在文件 `sys/conf/options.<架构>` 中定义。

### `device name [, name [...]]` 与 `devices name [, name [...]]`

配置指定的设备以包含到内核映像中。所有体系结构通用的设备在文件 `sys/conf/files` 中定义。特定于体系结构 arch 的设备在文件 `sys/conf/files.<架构>` 中定义。

### `env filename`

指定包含内核环境定义的文件名。

内核将使用在引导时由 loader(8) 为其准备的环境来增强这个编译到内核中的环境。在 loader(8) 环境中指定的环境变量将优先于在 filename 中指定的环境变量，而在动态环境中指定的环境变量将优先于这两者。

可以在静态环境中指定 `loader_env.disabled=1` 来禁用 loader(8) 环境。禁用 loader(8) 环境应谨慎进行，并充分考虑它是否提供了系统正常引导所需的环境变量。

可以在 loader(8) 环境中指定 `static_env.disabled=1` 来禁用静态环境的使用。如果在 loader(8) 环境处理之后的任何环境中指定此选项，则无效。此选项不能与 `loader_env.disabled` 同时使用。

此指令对于不从 loader(8) 启动的嵌入式环境中设置内核可调参数很有用。

所有 env 和 envvar 指令将被处理并以相反的出现顺序添加到静态环境中，以便后指定的变量正确覆盖先指定的变量。请注意，在 filename 中，给定变量的首次出现将是内核看到的第一个变量，有效地遮蔽了 filename 内同一变量的任何后续出现。

### `envvar setting`

指定要添加到内核编译时环境中的单个环境设置。setting 必须采用 `名称=值` 的形式。名称和值中都支持可选的引号。

所有 env 和 envvar 指令将被处理，并以相反的出现顺序添加到静态环境中，以便后指定的变量正确覆盖先指定的变量。

### `files filename`

指定一个文件，该文件包含特定于该内核配置文件的文件列表（类似于 files.<架构>）。

### `hints filename`

指定一个文件，用于从中加载静态设备配置规范。从 FreeBSD 5.0 开始，内核在引导时读取系统的设备配置（参见 device.hints(5)）。此指令配置内核使用 filename 中列出的静态设备配置。

此静态设备配置中提供的提示将按照遇到的顺序被覆盖。编译到内核的环境中的提示优先于编译到内核的提示，而为内核准备的 loader(8) 环境中的提示优先于编译到内核的环境中的提示。

若动态环境可用，所有编译到内核的提示如果没有在动态环境中被覆盖，则将被添加到动态环境中。然后，动态环境将用于所有提示的搜索。

可以在编译到内核的环境或 loader(8) 环境中指定 `static_hints.disabled=1` 来禁用这些提示文件的使用。如果在 loader(8) 环境处理之后的任何环境中指定此选项，则无效。

文件 filename 必须符合 device.hints(5) 指定的语法。可存在多条 hints 行。生成的提示将是按相反顺序连接的文件，以便后面文件中的提示正确覆盖前面文件中的提示。

### `ident name`

内核名称应设置为 name。至少需要一条 ident 指令。

### `include filename`

从文件 filename 读取后续文本，并在 filename 处理成功后返回到当前文件。

### `includeoptions filename`

指定一个文件，该文件包含特定于该内核配置文件的附加选项列表。这对于需要添加自定义选项的构建环境非常有用，并且通常与 makeoption 指令一起使用。

### `machine arch [cpuarch]`

指定正在编译内核的机器的体系结构。arch 的合法值包括：

* `arm64` - 64 位 ARM 应用体系结构
* `arm` - ARM 体系结构
* `amd64` - AMD x86-64 体系结构
* `i386` - 基于 Intel x86 的 PC 体系结构
* `powerpc` - IBM PowerPC 体系结构
* `riscv` - RISC-V 体系结构

如果指定了参数 cpuarch，它会将 config(8) 指向机器的 CPU 体系结构。当未指定 cpuarch 时，假定它与 arch 相同。arch 对应于 MACHINE。cpuarch 对应于 `MACHINE_ARCH`。

内核配置文件只能有一条 machine 指令，除非第二个指令与第一个指令中的 machine 参数完全匹配。

### `makeoption options` 与 `makeoptions options`

将选项添加到生成的 makefile 中。

options 参数是一个或多个选项规范的逗号分隔列表。每个选项规范的形式为：

```ini
Make变量名[=值]
Make变量名+=值
```

并将适当的 make(1) 变量定义插入到生成的 makefile 中。如果只指定了 make(1) 变量的名称，则假定其值为空字符串。

请注意，由于公共 makefile 在处理配置文件后覆盖了 CFLAGS 变量，因此无法直接通过 `makeoptions` 自定义 CFLAGS。尽管如此，可以使用变量 `CONF_CFLAGS` 指定自定义编译器标志。在公共 makefile 设置了后者之后，其内容将附加到 `CFLAGS`，可覆盖它们的编译标志。

示例：

```ini
makeoptions MYMAKEOPTION="foo"
makeoptions MYMAKEOPTION+="bar"
makeoptions MYNULLMAKEOPTION
makeoptions CONF_CFLAGS+="-DSOME_CONTROLLING_MACRO"
```

### `maxusers number`

此可选指令用于配置某些内核数据结构的大小。参数 number 可以是 0（默认值）或大于等于 2 的整数。值为 0 表示内核应根据可用物理内存的大小配置其数据结构。如果请求自动配置，内核将为此可调参数设置一个介于 32 和 384 之间的值（对于 32 位系统），或根据可用内存为 64 位系统设置更高的值。

如 tuning(7) 中所述，也可以在引导时使用 loader(8) 设置此可调参数。

### `nocpu cputype`

从先前选定的 CPU 列表中移除指定的 CPU。此指令可用于取消使用 include 包含的文件中 cpu 指令的效果。

### `nodevice name [, name [...]]` `nodevices name [, name [...]]`

从先前选定的设备列表中移除指定的设备。此指令可用于取消使用 include 包含的文件中 device 或 devices 指令的效果。

### `nomakeoption name` `nomakeoptions name`

从内核构建中移除先前定义的 make(1) 选项 name。此指令可用于取消使用 include 包含的文件中 makeoption 指令的效果。

### `nooption name [, name [...]]` `nooptions name [, name [...]]`

从先前定义的选项列表中移除指定的内核选项。此指令可用于取消使用 include 包含的文件中 option 或 options 指令的效果。

### `option optionspec [, optionspec [...]]` `options optionspec [, optionspec [...]]`

编译时内核选项添加到内核构建中。每个选项规范的形式为：

```ini
名称[=值]
```

如果未指定值，则假定为 NULL。所有体系结构通用的选项在文件 `sys/conf/options` 中指定。特定于体系结构 arch 的选项在文件 `sys/conf/options.<架构>` 中指定。

### 文件

* `sys/compile/NAME` - 根据内核配置创建的编译目录
* `sys/conf/Makefile.arch` - 体系结构 arch 的 Makefile 片段
* `sys/conf/files` - 所有体系结构通用的设备
* `sys/conf/files.arch` - 体系结构 arch 的设备
* `sys/conf/options` - 所有体系结构通用的选项
* `sys/conf/options.arch` - 体系结构 arch 的选项

### 参见

* kenv(1)
* make(1)
* device.hints(5)
* loader.conf(5)
* config(8)
* kldload(8)
* loader(8)

Samuel J. Leffler 和 Michael J. Karels, *Building 4.4BSD Kernels with Config*.

### 历史

config(8) 实用程序首次出现在 4.1BSD 中，随后在 4.4BSD 中进行了修订。

内核配置机制在 FreeBSD 4.0 和 FreeBSD 5.0 中进一步演进，转向支持动态内核配置的体系结构。

FreeBSD ports 15.0 - 2025 年 6 月 13 日

## 附录：man [device.hints(5)](https://man.freebsd.org/cgi/man.cgi?query=device.hints) 中文

以下是对 device.hints(5) 手册页的中文翻译整理，该页面介绍了设备资源提示文件的格式与用法：

### 名称

device.hints - 设备资源提示

### 描述

在系统即将启动时，引导加载程序 loader(8) 会读取 `device.hints` 文件，并将其内容传递给内核。`device.hints` 文件包含各种变量，用于控制内核的引导行为。这些变量通常是设备提示（device hints），但也可以包含任何内核可调参数值。

该文件每行包含一个变量。以 `#` 字符开头的行是注释，将被引导加载程序忽略。

文件被引导加载程序读取后，你可以使用 `show` 命令检查变量，并可以使用引导加载程序的 `set` 和 `unset` 命令添加新变量、修改现有变量或删除变量（参见 loader(8)）。

当系统启动后，你可以使用 kenv(1) 命令转储这些变量。

### 设备提示

设备提示变量由设备驱动程序用于设置设备。它们最常被 ISA 设备驱动程序用于指定驱动程序在何处探测相关设备，以及它将尝试使用哪些资源。

设备提示行的格式如下：

```ini
hint.驱动名称.单元编号.关键字="值"
```

其中：

* `driver` 是设备驱动程序的名称
* `unit` 是单元编号
* `keyword` 是提示的关键字

关键字可以是：

| 关键字        | 说明                   |
| ---------- | -------------------- |
| `at`       | 指定设备所连接的总线           |
| `port`     | 指定设备要使用的 I/O 端口的起始地址 |
| `portsize` | 指定设备使用的端口数量          |
| `irq`      | 要使用的中断线路号            |
| `drq`      | DMA 通道号              |
| `maddr`    | 指定设备使用的物理内存地址        |
| `msize`    | 指定设备使用的物理内存大小        |
| `flags`    | 设置设备的各种标志位           |
| `disabled` | 可设置为 `1` 来禁用设备       |

设备驱动程序可能需要一行到多行带有这些关键字的提示行，并且可能通过 resource\_int\_value(9) 接受此处未列出的其他关键字。请查阅各个设备驱动程序的联机手册以了解可用的关键字及其可能的值。

### 文件

* `/boot/device.hints` - 设备资源提示文件
* `/sys/ARCH/conf/GENERIC.hints` - GENERIC 内核的示例资源提示
* `/sys/ARCH/conf/NOTES` - 关于内核配置文件和设备资源提示的说明

### 示例

#### 设置 ISA 总线上 uart(4) 驱动程序的资源

```ini
hint.uart.0.at="isa"
hint.uart.0.port="0x3F8"
hint.uart.0.flags="0x10"
hint.uart.0.irq="4"
```

#### 禁用 ACPI 驱动程序

```ini
hint.acpi.0.disabled="1"
```

#### 设置可调参数变量

```ini
vm.pmap.pg_ps_enabled=1
```

### 参见

* kenv(1)
* loader.conf(5)
* loader(8)
* resource\_int\_value(9)

### 历史

`device.hints` 文件首次出现在 FreeBSD 5.0 中。

FreeBSD ports 15.0 - 2019 年 11 月 19 日

## 课后习题

1. 查找 FreeBSD 15.0-RELEASE 的 amd64 GENERIC 配置文件，修改其中的 ident 名称并添加自定义设备驱动选项，使用 config(8) 工具构建该配置文件并验证其语法正确性。分析修改的选项如何影响内核编译产物的大小和功能范围。
2. 创建一个基于 MINIMAL 的精简内核配置文件，移除至少 3 个你认为非必需的功能模块，尝试在 QEMU 中启动该内核，记录遇到的问题并解释这些模块在系统中的实际作用。思考这种精简对系统安全性和灵活性的影响。
3. 基于 2，尝试实现一个最精简的 FreeBSD iso 和 img 镜像文件，并支持 UEFI、ZFS 功能。可参考 release 目录中的构建工具，需先完成内核和 world 的构建。
