11.5. 高级主题
Linux 兼容层是一项正在进行的工作。请参考 FreeBSD Wiki - Linuxulator 获取更多信息。
所有与 Linux 有关的 sysctl(8) 变量的列表可以在 linux(4) 中找到。
一些应用程序要求挂载特定的文件系统。这通常由 /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) 创建的基本系统子目录,参考上述说明。
如果一个 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):
% brandelf -t Linux my-linux-elf-binary
11.5.3. 安装基于 RPM 的 Linux 应用程序
要安装基于 RPM 的 Linux 应用程序,首先要安装软件包或 port archivers/rpm4。安装完毕后,就可以用 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) 中嵌入了这一抽象层。
传统上,UNIX® 加载器会通过检查幻数(通常是文件的前 4 个或前 8 个字节)来确定其是否是系统已知的二进制文件。如果是,就调用二进制加载器。
如果不是,execve(2) 调用就会返回错误。shell 会试图以 shell 命令的形式执行它。“不论当前使用哪种shell”,均默认假定这是一个 shell 命令。
随后,被开发者修改过的 sh(1) 检查前两个字符。如果是 :\n
,则转而调用shell csh(1) 。
FreeBSD 有一份加载器列表,而非单一的加载器,并能降级回退到 #!
加载器来执行 shell 解释器或者 shell 脚本。
为了提供对 Linux 应用程序二进制接口(ABI)的支持 ,FreeBSD 将这个幻数视为一个二进制 ELF 文件。ELF 加载会查找专用的 brand,它在 ELF 二进制文件中是一个注释部分,但在 SVR4/Solaris™ 的 ELF 二进制中并不存在。
为了使 Linux 二进制文件发挥作用,必须使用 brandelf(1) 为 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) 命令,以确保 Linux 二进制文件无法知道它们并非是在 Linux 上运行。
事实上,FreeBSD 内核提供了一个 Linux 内核。 FreeBSD 系统调用表条目 和 Linux 系统调用表条目在由内核提供的服务的各种底层功能方面是相同的:文件系统操作、虚拟内存操作、signal delivery、System V 进程间通信等等。唯一的区别是,FreeBSD 的二进制程序得到 FreeBSD 的 胶水 函数,而 Linux 的二进制程序得到 Linux 的 胶水 函数。FreeBSD 的胶水函数是静态链 接到内核的,而 Linux 的胶水函数既可以是静态链接的,也可以通过内核模块访问。
从技术上讲,这并不是真正的仿真,而是一种应用程序二进制接口(ABI)的实现。有时这被称为“Linux 仿真”,只不过过去并没有其他适合的词来描述罢了。说 FreeBSD 运行 Linux 二进制程序是不正确的,因为这些代码并没有在 FreeBSD 系统上重新编译。