11.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 脚本挂载的文件系统对 chroot 或 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 主机上),因此不再可能将仿真功能静态地链接到一个定制内核中。

11.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 二进制文件,而不需要任何额外的工作。

11.5.2.Linux ELF 二进制文件的 brand

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

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

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

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

11.5.3. 安装基于 RPM 的 Linux 应用程序

要安装基于 RPM 的 Linux 应用程序,首先要安装软件包或 port archivers/rpm4open in new window。安装完毕后,就可以用 root 执行这个命令来安装 .rpm

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

如果有必要,把已安装的 ELF 二进制文件打上 brand。请注意,这将妨碍卸载的彻底性。

11.5.4. 配置主机名解析器

如果 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

11.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 做了一个修改,检查前两个字符,如果它们是 :,它就调用 csh(1)open in new window shell 来代替。

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

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

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

# brandelf -t Linux file

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

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

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

Linux 模式下,动态地 重新 进行查找。这实际上等同于文件系统挂载的 union。首先,尝试在 /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 二进制文件是不正确的,因为这些代码没有被编译进去。

Loading...