当前位置: 首页 > 文档资料 > FreeBSD 使用手册 >

第 11 章 Linux® 二进制兼容模式

优质
小牛编辑
211浏览
2023-12-01
Restructured and parts updated by Jim Mock. Originally contributed by Brian N. Handy 和 Rich Murphey.

11.1. 概述

FreeBSD 提供了与 Linux® 32-bit 二进制兼容,允许用户在 FreeBSD 系统上安装和运行大多数的 32-bit Linux®二进制程序而无需做任何修改。 据说在某些情况下, FreeBSD 上运行的32-bit Linux® 二进制程序能有更好的表现。

然而, 仍然有一些 Linux® 操作系统特有的功能在 FreeBSD上并不被支持。 例如, 要是 Linux® 程序过度地使用了诸如启用虚拟 8086 模式 i386™ 特有的调用,则无法在 FreeBSD 上运行。 另外, 目前还不支持 64-bit 的 Linux®二进制程序。

读完这章,您将了解到:

  • 如何在 FreeBSD 系统中启用 Linux® 二进制兼容模式。

  • 如何安装额外的 Linux® 共享库。

  • 如何在 FreeBSD 上安装 Linux® 应用程序。

  • FreeBSD 上 Linux® 兼容模式的实现细节。

在阅读这章之前,您应该知道:

  • 知道如何安装 额外的第三方软件。

11.2. 配置 Linux® 二进制兼容模式

默认情况下, Linux® 库并没有被安装而且 Linux®二进制兼容模式也没有被启动。 Linux® 库可以通过手动安装或者使用 FreeBSD 的 Ports Collection。

安装 emulators/linux-base-f10 包或者port 是最容易在 FreeBSD 系统上获得一套基本的 Linux® 库的方法。使用如下方法安装 port:

# cd /usr/ports/emulators/linux_base-f10# make install distclean

安装完成以后, 加载 linux模块启用 Linux® 二进制兼容模式:

# kldload linuxuserinput>

查看模块是否已经被加载:

% kldstatId Refs AddressSize Name 12 0xc0100000 16bdb8   kernel 71 0xc24db000 d000 linux.ko

/etc/rc.conf 中加入以下这行后Linux® 兼容模式便会在系统启动时自动开启:

linux_enable="YES"

想要在自制内核中静态链接 Linux® 二进制兼容支持的用户可以在自定义的内核配置文件中加入options COMPAT_LINUXliteral>。然后按照 第 9 章 配置FreeBSD的内核中所描述的方法编译并安装新内核。

11.2.1. 手动安装额外的库

在配置了 Linux® 兼容模式之后,如果某个 Linux® 应用程序依然提示找不到共享库,需先找出此 Linux® 二进制程序需要的共享库再手动安装。

在 Linux® 系统上使用 ldd找出应用程序所需的共享库文件。 比如,在安装有 Doom 的 Linux®系统上运行如下的命令列出 linuxdoom所需用到的共享库文件:

% ldd linuxdoomlibXt.so.3 (DLL Jump 3.1) => /usr/X11/lib/libXt.so.3.1.0libX11.so.3 (DLL Jump 3.1) => /usr/X11/lib/libX11.so.3.1.0libc.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

如果已经有了一个与 ldd 输出中第一列的主修订号相同的 Linux® 共享库文件,则不再需要复制最后那列文件, 现有的共享库应该可以正常使用。如果是更新版本的共享库通常建议复制。 只要有符号链接指向新的版本, 那么就可以删除旧版的了。

比如, 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.2.2. 安装 Linux® ELF二进制程序

ELF 二进制程序有时需要额外的步骤。当未被标记的 ELF 二进制程序被执行的时候,会生成如下的错误信息:

% ./my-linux-elf-binaryELF binary type not knownAbort

为了帮助 FreeBSD 内核分辨 FreeBSD ELF 二进制程序和 Linux® 二进制程序, 请使用 brandelf(1)

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

由于现在的 GNU 工具链能自动把适当的标记信息写入 ELF二进制程序中,这个步骤通常不是必须做的。

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

安装基于 Linux® RPM 的应用程序,首先需要安装 archivers/rpm 包或者 port。安装好之后 root用户就能使用此命令安装 .rpm 了:

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

如有必要的话使用 brandelf 标记安装好的ELF 二进制程序。 注意此项安装将无法干净卸载。

11.2.4. 配置主机名解析器

如果 DNS 不能正常工作或是出现以下的错误信息:

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

请参照此方法配置 /compat/linux/etc/host.conf

order hosts, bindmulti on

这里指定了先查询 /etc/hosts再查询 DNS。 如果/compat/linux/etc/host.conf 不存在的话,Linux® 程序便会读取 /etc/host.conf并提示与 FreeBSD 的语法不兼容。 如果没有在/etc/resolv.conf 文件中配置域名服务器,可以删除 bind

11.3. 高级主题

此章节将讲述是 Linux® 二进制兼容如何工作的,内容基于 Terry Lambert <tlambert@primenet.com> (Message ID:<199906020108.SAA07001@usr09.primenet.com>)发表在 FreeBSD 闲聊邮件列表 的邮件。

FreeBSD 有一个叫 “execution class loader” 的抽象层。它被嵌入进了 execve(2) 系统调用。

历史上 UNIX® 加载器会依靠查看魔数(通常是文件的开头 4 至 8 个字节)来确认是否是系统已知的的二进制程序,如果是的话, 就会调用二进制程序加载器。

如果它不是二进制类型的程序, execve(2) 调用会返回一个错误,shell 则会把它当作 shell 命令执行。“不论当前是哪一种 shell” 都会默认做出此种假设。

随后, sh(1) 会检查开头的两个字符,如果它们是 :\n, 那么就调用 csh(1)

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® 二进制程序可以加载并执行 FreeBSD 二进制程序,如果当前没有相应的 Linux® 二进制程序,可以在 /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® 二进制程序并不确切,因为当时代码并还没有被编译进去。