21.1 Linux 兼容层实现
一个常见的误解是将 FreeBSD 的 Linux 兼容层当作虚拟机,认为这样做会降低软件的运行效率。实际情况是不仅不会变慢,而且有些软件的运行速度甚至会比在 Linux 中更快,运行效率更高。因为它不是模拟器,也不是转译层,而是 Linux ABI 的一种实现。
https://cgit.freebsd.org/src/tree/sys/compat/linux/linux_file.c
从这里不难看出,其所做的事情只有一项:识别 Linux 系统调用,然后找到对应的 FreeBSD 系统调用实现。将原本的 Linux 系统调用请求转交到 FreeBSD 系统调用的入口进行处理。
何为 Linuxulator
既然要谈 FreeBSD 兼容 Linux 应用程序,那么一切都要从 Linuxulator 开始说起。Linuxulator 的字面含义是 Linux Emulator,这很容易让人联想到它是 Linux 模拟器。然而 Linuxulator 并不是传统意义上的模拟器,其本身甚至都不是具有单独可执行文件的 FreeBSD 应用程序。Linuxulator 只是 FreeBSD 官方文档中对某个 FreeBSD 内核模块的通俗称呼,该内核模块的正式名称为 linux。
下面先查看一下 linux 内核模块对应的 man 页面。
man 页面
查看 FreeBSD 中 Linux 兼容层相关的内核接口说明:
$ man 4 linuxman 4 linux 的输出内容翻译如下:
LINUX(4) FreeBSD 内核接口手册 LINUX(4)
名称 linux – Linux ABI 支持
简介 要在启动时启用 Linux ABI,请在 rc.conf(5) 中添加以下行:
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,可以使用 Portemulators/linux_base-c7或对应的包,或者通过sysutils/debootstrap安装debootstrap(8)。为避免在启动时挂载 Linux 特有的文件系统,可以在
rc.conf(5)文件中添加以下行:
SYSCTL 变量 以下变量既可作为 sysctl(8) 变量,亦可作为 loader(8) 可调参数使用:
compat.linux.debug启用调试消息。设置为0可静音。默认为3。设置为1时打印调试消息,报告未实现的功能(仅一次)。设置为2时类似于1,但也会打印已实现但未测试的功能的消息(仅一次)。设置为3及更高时,类似于2,但没有消息频率限制。compat.linux.default_openfilesLinux 应用程序的默认软打开文件资源限制。设置为-1即禁用该限制。默认为1024。compat.linux.emul_pathLinux 运行时环境的路径。默认为/compat/linux。compat.linux.osnameLinux 内核操作系统名称。默认为 "Linux"。compat.linux.osreleaseLinux 内核操作系统发布版本。不建议在非开发系统上修改此值,因为这可能会影响 Linux 程序的工作方式。已知某些版本的 GNU libc 会根据该值的不同使用不同的系统调用。compat.linux.oss_versionLinux 开放音频系统版本。默认为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 i386uname -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 内核的系统调用实现来回应 Linux 进程的系统调用请求。
注意
Linuxulator 是用 FreeBSD 内核的系统调用实现来回应 Linux 进程的系统调用请求。
换句话说,借助 Linuxulator 这一内核模块,FreeBSD 内核可以在行为上伪装成 Linux 内核,但实际运行 Linux 进程的仍然是 FreeBSD 内核,负责处理 Linux 系统调用的也依然是 FreeBSD 内核中的代码。
并且从 FreeBSD 内核的角度来看,通过 Linuxulator 运行的 Linux 进程与 FreeBSD 原生进程并无本质区别,Linux 进程本质上仍然是 FreeBSD 进程。
什么是 Linux 兼容层
路径转换机制使 Linux 进程在 / 之前查找 emul_path(默认为 /compat/linux)下的文件路径。例如,当 Linux 进程试图打开 /etc/passwd 时,它将真正访问 /compat/linux/etc/passwd,除非后者不存在。这被用来确保 Linux 进程加载 Linux 共享库,而不是类似名字的 FreeBSD 对应库,同时也提供了某些其他文件和虚拟文件系统的替代版本。
由于系统中本质上并不存在真实的 Linux 内核,FreeBSD 所声明的 Linux 内核版本并不具有实际意义,理论上甚至可以将其设置为 255.255。
但需要注意的是,不同的软件对内核版本有不同的要求,其所依赖和使用的接口也各不相同。例如,当所声明的 Linux 内核版本过低时,Arch Linux 可能无法进入 chroot 环境,并报错 kernel too old。
附录:为什么使用 Linux 兼容层并非“苦难哲学”
在系统启动后加载 kldload linux64、kldload linux 并不应受到责难或讽刺,正如荀子所言:“君子生非异也,善假于物也”(荀子[M].北京:中华书局,1954.)。
使用 Linux 兼容层亦是如此,这并不存在荒唐或可笑之处。类似的技术包括 Linux 上使用 Wine 或 CrossOver,乃至 ReactOS;以及 Windows 平台上的 Linux 兼容层和 Android 兼容层,这些方案都得到了广泛应用。
最后更新于
这有帮助吗?