10.5. 高级主题
Linux 兼容层是一项正在进行的工作。有关更多信息请参阅 FreeBSD Wiki - Linuxulator 。
所有与 Linux 相关的 sysctl(8) 旋钮列表都可以在 linux(4) 中找到。
一些应用程序要求挂载特定的文件系统。这一般由 /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) 创建的基本系统子目录,请参考上面的说明。
如果某个 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 中。复制完后,建立符号链接 (Symbolic link) 至输出结果第一栏的名称。此示例将在 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-二进制程序
ELF binary type not known
Abort
为了帮助 FreeBSD 内核区分是 FreeBSD ELF 二进制程序 还是 Linux 二进制程序,可以使用 brandelf(1):
% brandelf -t Linux my-linux-elf--binary
10.5.3.安装基于 Linux RPM 的应用程序
要安装基于 Linux RPM 的应用程序,首先安装 archivers/rpm4 套件或 Port 。安装后,root
可以使用此命令安装 .rpm:
# cd /compat/linux
# rpm2cpio < /path/to/linux.archive.rpm | cpio -id
如有必要,brandelf 已安装的 ELF 二进制程序。注意,这将无法干净地卸载他。
10.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 。
10.5.5.其他
这一节描述了 Linux 二进制兼容性的工作原理,它基于 Terry Lambert 写给 FreeBSD 聊天邮件列表 的一封邮件 tlambert@primenet.com (邮件 ID: 199906020108.SAA07001@usr09.primenet.com)。
FreeBSD 有一个叫做“执行类加载器”的抽象概念。这是一个楔入 execve(2) 系统调用的工具。
以前,UNIX® 加载器检查幻数(通常是文件的前 4 或 8 个字节),看它是否是系统已知的二进制文件,如果是,则调用二进制加载器。
如果它是系统未知的二进制类型,execve(2) 调用就会返回失败,并且 shell
尝试将其作为 shell 命令开始执行。该假设是“无论当前的 shell 是什么”的默认设置。
后来,对 sh(1) 进行了修改,检查前两个字符,如果它们是 :\n
,则改为调用 csh(1) shell。
FreeBSD 有一个加载器列表,而不是单个加载器,并带有 #!
用于运行 shell 解释器或 shell 脚本的加载程序。
对于 Linux ABI 支持,FreeBSD 将幻数视为 ELF 二进制文件。ELF 加载程序会寻找一个专门的品牌,它是 ELF 映像中的一个注释部分,并且在 SVR4/Solaris™
ELF 二进制文件中不存在。
要使 Linux 二进制文件正常运行,必须使用 brandelf(1) 将它们标记为 Linux 类型:
# brandelf -t Linux file
当 ELF 加载器看到 Linux
品牌时,加载器会替换 proc
结构中的一个指针。所有系统调用都通过这个指针进行索引。此外,该进程被标记为 signal trampoline
的陷阱向量进行特殊处理,以及由 Linux 内核模块处理的其他几个(次要)修复。
Linux 系统调用向量包含一个 sysent[]
条目列表,其地址位于内核模块中。
当一个系统调用被 Linux 二进制文件调用时,陷阱代码会解除对 proc
结构中系统调用函数指针的引用,并获得 Linux 而不是 FreeBSD 的系统调用入口点。
Linux模式下动态 reroots 查询。这实际上等同于文件系统挂载的 union
。首先,尝试在 /compat/linux/original-path 中查找文件。如果失败,则在 /original-path 中进行查找。这确保了需要其他二进制文件的二进制文件能够运行。例如,Linux 工具链都可以在 Linux ABI 支持下运行。这也意味着,如果没有相应的 Linux 二进制文件存在,Linux 二进制文件可以加载和执行 FreeBSD 二进制文件,并且可以在 /compat/linux 目录树下放置 uname(1) 命令,以确保 Linux 二进制文件无法知道它们不是在 Linux 上运行。
实际上,在 FreeBSD 的内核中就有一个 Linux 内核。实现内核所提供的所有服务的各种底层函数与 FreeBSD 系统调用表项和 Linux 系统调用表项都是相同的:件系统操作,虚拟内存操作,信号传递,以及 System V IPC
。
唯一的区别是 FreeBSD 二进制文件得到 FreeBSD 的 glue
函数,而 Linux 二进制文件得到 Linux 的 glue
函数。FreeBSD 的 glue
函数是静态链接到内核的,而 Linux 的 glue
函数可以是静态链接的,也可以通过内核模块访问。
从技术上讲,这并不是真正的仿真,而是 ABI 的实现。它有时被称为“Linux 仿真”,因为这个实现是在没有其他词来描述所发生的事情的时候完成的。说 FreeBSD 运行 Linux 二进制文件是不正确的,因为这些代码没有被编译进去。