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.补充说明

本节根据 Terry Lambert 用 [email protected] 发送给 FreeBSD 聊天邮箱列表的一封电子邮件(邮件 ID:[email protected])的内容,介绍了 Linux 二进制兼容层的工作原理。

FreeBSD 有一个叫做“可执行类加载器(execution class loader)”的抽象层,系统调用 execve(2)open in new window 中嵌入了这一抽象层。

传统上,UNIX® 加载器会通过检查幻数(通常是文件的前 4 个或前 8 个字节)来确定其是否是系统已知的二进制文件。如果是,就调用二进制加载器。

如果不是,execve(2)open in new window 调用就会返回错误。shell 会试图以 shell 命令的形式执行它。“不论当前使用哪种shell”,均默认假定这是一个 shell 命令。

随后,被开发者修改过的 sh(1)open in new window 检查前两个字符。如果是 :\n,则转而调用shell csh(1)open in new window

FreeBSD 有一份加载器列表,而非单一的加载器,并能降级回退到 #! 加载器来执行 shell 解释器或者 shell 脚本。

为了提供对 Linux 应用程序二进制接口(ABI)的支持 ,FreeBSD 将这个幻数视为一个二进制 ELF 文件。ELF 加载会查找专用的 brand,它在 ELF 二进制文件中是一个注释部分,但在 SVR4/Solaris™ 的 ELF 二进制中并不存在。

为了使 Linux 二进制文件发挥作用,必须使用 brandelf(1)open in new window 为 Linux 二进制文件打上 brand

# brandelf -t Linux file

当 ELF 加载器看到 Linux 的 brand 时,加载器便会替换 proc\ 结构体中的一个指针。所有的系统调用都通过此指针进行索引。此外,系统对进程进行标记,用于信号跳板(signal trampoline)代码的陷阱向量的处理,还有一些其他细微修补由 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 系统调用表条目在由内核提供的服务的各种底层功能方面是相同的:文件系统操作、虚拟内存操作、signal delivery、System V 进程间通信等等。唯一的区别是,FreeBSD 的二进制程序得到 FreeBSD 的 胶水 函数,而 Linux 的二进制程序得到 Linux 的 胶水 函数。FreeBSD 的胶水函数是静态链 接到内核的,而 Linux 的胶水函数既可以是静态链接的,也可以通过内核模块访问。

从技术上讲,这并不是真正的仿真,而是一种应用程序二进制接口(ABI)的实现。有时这被称为“Linux 仿真”,只不过过去并没有其他适合的词来描述罢了。说 FreeBSD 运行 Linux 二进制程序是不正确的,因为这些代码并没有在 FreeBSD 系统上重新编译。

Loading...