# 21.1 Linux 兼容层架构

一个常见的技术认知误区是将 FreeBSD 的 Linux 兼容层等同于虚拟机，进而认为该机制会不可避免地降低软件运行效率。然而，实证观察表明，该兼容层不仅不会引入显著的性能开销，在某些特定场景下，部分软件的运行性能甚至优于其在原生 Linux 环境中的表现。这一特性的根本原因在于：该兼容层既非指令集模拟器，亦非二进制转译层，而是 Linux 应用程序二进制接口（Application Binary Interface，ABI）的一种系统级实现。

<https://cgit.freebsd.org/src/tree/sys/compat/linux/linux_file.c>

从该内核源代码文件中可以清晰地观察到，该模块的核心功能具有单一性：其主要职责是识别 Linux 系统调用，随后定位并映射到与之对应的 FreeBSD 系统调用实现，并将原始的 Linux 系统调用请求转发至 FreeBSD 系统调用入口点进行处理。

## 何为 Linuxulator

若要深入理解 FreeBSD 对 Linux 应用程序的兼容机制，首先需要考察 Linuxulator 这一核心组件。Linuxulator 这一术语的字面含义为“Linux Emulator”，该名称极易引起误解，使人将其与传统意义上的指令集模拟器相混淆。然而，Linuxulator 并非传统模拟器范畴，它甚至不是一个具有独立可执行文件形态的 FreeBSD 用户空间应用程序。确切地说，Linuxulator 只是 FreeBSD 官方文档中对某一特定内核模块的非正式称谓，该内核模块的正式标识符为 `linux`。

下面先查看一下 `linux` 内核模块对应的 `man` 页面。

### man 页面

查看 FreeBSD 中 Linux 兼容层相关的内核接口说明：

```sh
$ man 4 linux
```

`man 4 linux` 的输出内容翻译如下：

***

**LINUX(4)** FreeBSD 内核接口手册 **LINUX(4)**

**名称** linux – Linux ABI 支持

**简介** 要在启动时启用 Linux ABI，请在 `rc.conf(5)` 中添加以下行：

```sh
linux_enable="YES"
```

**描述** Linux 内核模块提供了有限的 Linux ABI（Application Binary Interface，ABI）兼容性，使得无需虚拟化和仿真即可运行许多未经修改的 Linux 应用程序。提供的功能包括：

* Linux 到原生系统调用的转换
* Linux 特有的系统调用
* Linux 进程的特殊信号处理
* 路径转换机制
* Linux 特有的虚拟文件系统

  路径转换机制使得 Linux 进程在查找文件路径时，会首先在 `emul_path`（默认为 `/compat/linux`）下查找，然后才是 `/`。例如，当一个 Linux 进程尝试打开 `/etc/passwd` 时，它会首先访问 `/compat/linux/etc/passwd`，如果兼容路径不存在，则回退到 `/etc/passwd`。此机制确保 Linux 进程加载的是 Linux 共享库，而非它们对应的 FreeBSD 版本，并且为某些其他文件和虚拟文件系统提供替代版本。

  要将 Linux 共享库和系统文件安装到 `/compat/linux`，可以使用 Port `emulators/linux_base-c7` 或对应的包，或者通过 `sysutils/debootstrap` 安装 `debootstrap(8)`。

  为避免在启动时挂载 Linux 特有的文件系统，可以在 `rc.conf(5)` 文件中添加以下行：

```sh
linux_mounts_enable="NO"
```

**SYSCTL 变量** 以下变量既可作为 `sysctl(8)` 变量，亦可作为 `loader(8)` 可调参数使用：

* `compat.linux.debug` 启用调试消息。设置为 `0` 可静音。默认为 `3`。设置为 `1` 时打印调试消息，报告未实现的功能（仅一次）。设置为 `2` 时类似于 `1`，但也会打印已实现但未测试的功能的消息（仅一次）。设置为 `3` 及更高时，类似于 `2`，但没有消息频率限制。
* `compat.linux.default_openfiles` Linux 应用程序的默认软打开文件资源限制。设置为 `-1` 即禁用该限制。默认为 `1024`。
* `compat.linux.emul_path` Linux 运行时环境的路径。默认为 `/compat/linux`。
* `compat.linux.osname` Linux 内核操作系统名称。默认为“Linux”。
* `compat.linux.osrelease` Linux 内核操作系统发布版本。不建议在非开发系统上修改此值，因为这可能会影响 Linux 程序的工作方式。已知某些版本的 GNU libc 会根据该值的不同使用不同的系统调用。
* `compat.linux.oss_version` Linux 开放音频系统版本。默认为 `198144`。
* `compat.linux.preserve_vstatus` 设置为 1 时，防止 Linux 应用程序重置 `termios(4)` 的 VSTATUS 设置。从用户角度来看，这使得 `SIGINFO` 对 Linux 可执行文件有效。默认为 `1`。
* `compat.linux.setid_allowed` 启用处理新进程镜像文件的用户 ID 和组 ID 设置模式位（set-user-ID 和 set-group-ID）。设置为 0 时，新的 Linux 镜像始终使用发出 `execve(2)` 调用的程序的凭证，而忽略镜像文件的模式。因为 FreeBSD 没有完全仿真 Linux 环境，缺失的功能可能会导致安全漏洞。默认为 `1`。

  `compat.linux32.emulate_i386` 在 x86\_64（amd64）环境中启用真实的 i386 Linuxulator 行为。例如，当设置为 `0` 时，即使 `uname` 是 i386 Linux 可执行文件，`uname -m` 也会返回“x86\_64”。当设置为 `1` 时，Linux i386 `uname -m` 会返回“i686”。默认为 `0`。

**文件** `/compat/linux` Linux 运行时环境 `/compat/linux/dev` 设备文件系统，参见 `devfs(5)` `/compat/linux/dev/fd` 文件描述符文件系统，挂载了 `linrdlnk` 选项，参见 `fdescfs(5)` `/compat/linux/dev/shm` 内存文件系统，参见 `tmpfs(5)` `/compat/linux/proc` Linux 进程文件系统，参见 `linprocfs(5)` `/compat/linux/sys` Linux 内核对象文件系统，参见 `linsysfs(5)`

**参见** `brandelf(1)`, `pty(4)`, `elf(5)`, `fdescfs(5)`, `linprocfs(5)`, `linsysfs(5)`, `tmpfs(5)`

**历史** Linux ABI 支持首次出现在 FreeBSD 2.1 中。对 amd64 二进制文件的支持首次出现在 FreeBSD 10.3 中。对 arm64 二进制文件的支持首次出现在 FreeBSD 12.0 中。

**BUG** 某些 Linux 特有的系统调用和系统调用参数的支持仍然缺失。

FreeBSD 14.2-RELEASE 2022 年 1 月 9 日 FreeBSD 14.2-RELEASE

***

综上所述，Linuxulator 与 WSL2 的虚拟机方案和 Wine 的二进制兼容层解释机制存在本质区别。

其核心原理是：FreeBSD 内核能够识别并拦截 Linux 进程的系统调用请求，将其映射到功能等价的 FreeBSD 系统调用，然后使用 FreeBSD 内核的实现来响应这些请求。

> **注意**
>
> **Linuxulator 的关键在于利用 FreeBSD 内核的系统调用来处理 Linux 进程的系统调用**。

因此，通过 Linuxulator 模块，FreeBSD 内核在系统调用接口上模拟 Linux 内核行为，但实际进程调度和执行仍由 FreeBSD 内核负责，处理系统调用的代码也是 FreeBSD 原生实现。

从进程管理角度看，通过 Linuxulator 运行的 Linux 进程在 FreeBSD 内核中被视为标准 FreeBSD 进程，与原生进程无异。

## 什么是 Linux 兼容层

由于 FreeBSD 系统中本质上并不存在真实的 Linux 内核，FreeBSD 所声明的 Linux 内核版本号仅具有标识意义，而不具备实际的内核功能约束；从理论上讲，该版本号甚至可以被设置为任意值，例如 `255.255`。

然而需要特别注意的是，不同的 Linux 软件对内核版本具有不同的最低要求，其依赖和使用的系统调用接口也存在差异。例如，当所声明的 Linux 内核版本过低时，Arch Linux 的 chroot 环境可能无法正常初始化，并返回 `kernel too old` 的错误信息。

## 附录：为什么使用 Linux 兼容层并非“苦难哲学”

在系统启动后加载 `kldload linux64`、`kldload linux` 并不应受到责难或讽刺，正如荀子所言：“君子生非异也，善假于物也。”（荀子. 荀子\[M]. 北京: 中华书局, 1954. 系统阐述“善假于物”的方法论，为技术兼容实践提供哲学依据。）

使用 Linux 兼容层亦是如此，这并不存在荒唐或可笑之处。类似的技术包括 Linux 上使用 Wine 或 CrossOver，乃至 [ReactOS](https://reactos.org/)；以及 Windows 平台上的 Linux 兼容层和 Android 兼容层，这些方案都得到了广泛应用。

## 课后习题

1. 查找 FreeBSD 源码中 `sys/compat/linux/` 目录下的 `linux_file.c`，分析 Linux 系统调用到 FreeBSD 系统调用的映射机制，并选取 3 个关键系统调用追踪其处理流程。
2. 修改 `compat.linux.osrelease` 为 2 个不同的 Linux 内核版本号，在兼容层中运行 `uname -a` 并测试 2 个不同 Linux 软件的兼容性，分析该设计如何能让用户在无真实 Linux 内核的情况下控制软件行为。
