10.5. 高级主题

Linux 兼容性层是一项正在进行的工作。请参考 FreeBSD Wiki - Linuxulatoropen in new window 获取更多信息。

所有与 Linux 有关的 sysctl(8)open in new window 旋钮的列表可以在 linux(4)open in new window 中找到。

一些应用程序要求挂载特定的文件系统。这通常由 /etc/rc.d/linux 脚本处理,但可以通过在 /etc/rc.conf 中添加这一行来禁用。

linux_mounts_enable="NO"

由rc脚本挂载的文件系统对 chroots 或 jail 内的 Linux 进程不起作用;如果需要,在 /etc/fstab 中配置它们。

devfs      /compat/linux/dev      devfs      rw,late                    0  0
tmpfs      /compat/linux/dev/shm  tmpfs      rw,late,size=1g,mode=1777  0  0
fdescfs    /compat/linux/dev/fd   fdescfs    rw,late,linrdlnk           0  0
linprocfs  /compat/linux/proc     linprocfs  rw,late                    0  0
linsysfs   /compat/linux/sys      linsysfs   rw,late                    0  0

由于 Linux 二进制兼容层已经获得了对运行32位和64位 Linux 二进制文件的支持(在64位x86主机上),因此不再可能将仿真功能静态地链接到一个自定义内核中。

10.5.1. 手动安装附加库

备注

对于用 debootstrap(8)open in new window 创建的基本系统子目录,用上面的说明代替。

如果一个Linux应用程序在配置了Linux二进制兼容性后抱怨缺少共享库,请确定Linux二进制需要哪些共享库,并手动安装它们。

在一个使用相同 CPU 架构的 Linux 系统中,ldd 可以用来确定应用程序需要哪些共享库。例如,要检查 linuxdoom 需要哪些共享库,可以从安装了 Doom 的 Linux 系统上运行这个命令。

% ldd linuxdoom
libXt.so.3 (DLL Jump 3.1) => /usr/X11/lib/libXt.so.3.1.0
libX11.so.3 (DLL Jump 3.1) => /usr/X11/lib/libX11.so.3.1.0
libc.so.4 (DLL Jump 4.5pl26) => /lib/libc.so.4.6.29

然后,将 Linux 系统输出的最后一列中的所有文件复制到 FreeBSD 系统的 /compat/linux 中。一旦复制完毕,为第一列中的名字创建符号链接。这个例子将导致在 FreeBSD 系统上出现以下文件。

/compat/linux/usr/X11/lib/libXt.so.3.1.0
/compat/linux/usr/X11/lib/libXt.so.3 -> libXt.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3.1.0
/compat/linux/usr/X11/lib/libX11.so.3 -> libX11.so.3.1.0
/compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29

如果一个 Linux 共享库已经存在,其主要修订号与 ldd 输出的第一列相匹配,则不需要将其复制到最后一列命名的文件中,因为现有的库应该可以工作。不过,如果共享库是一个较新的版本,建议复制它。旧的可以删除,只要符号链接指向新的就可以了。

例如,这些库已经存在于FreeBSD系统中。

/compat/linux/lib/libc.so.4.6.27
/compat/linux/lib/libc.so.4 -> libc.so.4.6.27

和 ldd 表示一个二进制文件需要一个更高的版本。

libc.so.4 (DLL Jump 4.5pl26) -> libc.so.4.6.29

由于现有库的最后一位数字只过了一两个版本,所以程序应该仍然可以使用稍旧的版本。然而,用较新的版本替换现有的 libc.so 是安全的。

/compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29

一般来说,只有在 FreeBSD 上安装 Linux 程序的前几次,才需要寻找 Linux 二进制文件所依赖的共享库。一段时间后,系统中就会有足够的 Linux 共享库,从而能够运行新安装的 Linux 二进制文件,而不需要任何额外的工作。

10.5.2. 打造Linux ELF二进制文件的品牌

FreeBSD 内核使用几种方法来确定要执行的二进制文件是否是 Linux 的: 它检查 ELF 文件头中的品牌, 寻找已知的 ELF 解释器路径, 并检查 ELF 注释; 最后, 默认情况下, 无品牌的 ELF 可执行文件被假定为 Linux。如果所有这些方法都失败了,试图执行该二进制文件可能会导致错误信息。

% ./my-linux-elf-binary
ELF binary type not known
Abort

为了帮助 FreeBSD 内核区分 FreeBSD ELF 二进制文件和 Linux 二进制文件, 可以使用 brandelf(1) 。

% brandelf -t Linux my-linux-elf-binary

10.5.3. 安装一个基于RPM的Linux应用程序

要安装一个基于 RPM 的 Linux 应用程序,首先要安装 archivers/rpm4 软件包或端口。一旦安装完毕,root 可以使用这个命令来安装一个.rpm。

# cd /compat/linux
# rpm2cpio < /path/to/linux.archive.rpm | cpio -id

如果有必要,把已安装的ELF二进制文件打上品牌。请注意,这将妨碍清洁卸载。

配置主机名解析器

如果DNS不工作或出现这个错误。

resolv+: "bind" is an invalid keyword resolv+:
"hosts" is an invalid keyword

配置 /compat/linux/etc/host.conf 如下。

order hosts, bind
multi on

这规定了 /etc/hosts 首先被搜索,DNS 其次被搜索。当 /compat/linux/etc/host.conf 不存在时, Linux 应用程序会使用 /etc/host.conf 并抱怨不兼容的 FreeBSD 语法。如果没有使用 /etc/resolv.conf 配置名字服务器,则删除 bind。

10.5.5. 杂项

这一节描述了 Linux 二进制兼容性的工作原理, 它基于 Terry Lambert 写给 FreeBSD 聊天邮件列表的一封邮件 [email protected] (邮件 ID: [email protected])。

FreeBSD 有一个叫做 "执行类加载器 "的抽象概念。这是一个楔入 execve(2)open in new window系统调用的工具。

历史上,UNIX® 加载器检查神奇的数字(通常是文件的前4或8个字节),看它是否是系统已知的二进制文件,如果是,就调用二进制加载器。

如果它不是系统的二进制类型,execve(2)open in new window调用就会返回失败,并且shell试图开始以shell命令的形式执行它。假设是默认的 "无论当前的shell是什么"。

后来,sh(1)open in new window做了一个黑客,检查前两个字符,如果它们是:\n,它就调用csh(1)open in new windowshell来代替。

FreeBSD有一个加载器列表,而不是单一的加载器,在运行shell解释器或shell脚本时,可以退到#!加载器。

对于 Linux ABI 的支持, FreeBSD 把这个神奇的数字看作是ELF二进制文件。ELF 装载器会寻找一个专门的品牌, 它是 ELF 映像中的一个注释部分, 在 SVR4/Solaris™ ELF 二进制文件上是不存在的。

要使Linux二进制文件发挥作用,必须使用brandelf(1)open in new window将其标记为Linux类型。

# brandelf -t Linux file

当ELF加载器看到 Linux 品牌时,加载器会替换 proc 结构中的一个指针。所有的系统调用都通过这个指针进行索引。此外,进程被标记为对信号蹦床代码的陷阱向量进行特殊处理,以及由Linux内核模块处理的其他几个(小的)修复。

Linux 系统调用向量包含,除其他外,一个 sysent[] 条目列表,其地址位于内核模块中。

当一个系统调用被 Linux 二进制文件调用时, 陷阱代码会解除对 proc 结构中系统调用函数指针的引用, 并获得 Linux 而不是 FreeBSD 的系统调用入口点。

Linux 模式下,动态地重新进行查找。这实际上等同于文件系统挂载的联合。首先,尝试在 /compat/linux/original-path 中查找文件。如果失败,则在 /original-path 中进行查找。这确保了需要其他二进制文件的二进制文件能够运行。例如,Linux 工具链都可以在 Linux ABI 支持下运行。这也意味着,如果没有相应的Linux 二进制文件存在,Linux 二进制文件可以加载和执行 FreeBSD 二进制文件,并且可以在 /compat/linux 目录树下放置 uname(1)open in new window 命令,以确保 Linux 二进制文件无法知道它们不是在 Linux 上运行。

实际上,在 FreeBSD 的内核中就有一个 Linux 内核。实现内核所提供的所有服务的各种底层函数与 FreeBSD 系统调用表项和 Linux 系统调用表项都是相同的:文件系统操作,虚拟内存操作,信号传递,以及 System V IPC。唯一的区别是,FreeBSD 的二进制文件得到 FreeBSD 的胶合函数,而 Linux 的二进制文件得到 Linux 的胶合函数。FreeBSD 的胶合函数是静态链接到内核的,而 Linux 的胶合函数可以是静态链接的,也可以通过内核模块访问。

从技术上讲,这并不是真正的仿真,而是 ABI 的实现。它有时被称为 "Linux 仿真",因为这个实现是在没有其他词来描述所发生的事情的时候完成的。说 FreeBSD 运行 Linux 二进制文件是不正确的,因为这些代码没有被编译进去。