11.3. 高级主题

此章节将讲述是 Linux® 二进制兼容如何工作的, 内容基于 Terry Lambert (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® 二进制程序并不确切, 因为当时代码并还没有被编译进去。

本文档和其它文档可从这里下载: ftp://ftp.FreeBSD.org/pub/FreeBSD/doc/.

如果对于FreeBSD有问题,请先阅读 文档,如不能解决再联系 <questions@FreeBSD.org>.

关于本文档的问题请发信联系 <doc@FreeBSD.org>.