1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
|
---
title: 第 11 章 Linux® 二进制兼容模式
part: 部分 II. 常见的任务
prev: books/handbook/printing
next: books/handbook/partiii
showBookMenu: true
weight: 14
params:
path: "/books/handbook/linuxemu/"
---
[[linuxemu]]
= Linux® 二进制兼容模式
:doctype: book
:toc: macro
:toclevels: 1
:icons: font
:sectnums:
:sectnumlevels: 6
:sectnumoffset: 11
:partnums:
:source-highlighter: rouge
:experimental:
:images-path: books/handbook/linuxemu/
ifdef::env-beastie[]
ifdef::backend-html5[]
:imagesdir: ../../../../images/{images-path}
endif::[]
ifndef::book[]
include::shared/authors.adoc[]
include::shared/mirrors.adoc[]
include::shared/releases.adoc[]
include::shared/attributes/attributes-{{% lang %}}.adoc[]
include::shared/{{% lang %}}/teams.adoc[]
include::shared/{{% lang %}}/mailing-lists.adoc[]
include::shared/{{% lang %}}/urls.adoc[]
toc::[]
endif::[]
ifdef::backend-pdf,backend-epub3[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
endif::[]
ifndef::env-beastie[]
toc::[]
include::../../../../../shared/asciidoctor.adoc[]
endif::[]
[[linuxemu-synopsis]]
== 概述
FreeBSD 提供了与 Linux(R) 32-bit 二进制兼容, 允许用户在 FreeBSD 系统上安装和运行大多数的 32-bit Linux(R) 二进制程序而无需做任何修改。 据说在某些情况下, FreeBSD 上运行的 32-bit Linux(R) 二进制程序能有更好的表现。
然而, 仍然有一些 Linux(R) 操作系统特有的功能在 FreeBSD 上并不被支持。 例如, 要是 Linux(R) 程序过度地使用了诸如启用虚拟 8086 模式 i386(TM) 特有的调用, 则无法在 FreeBSD 上运行。 另外, 目前还不支持 64-bit 的 Linux(R) 二进制程序。
读完这章,您将了解到:
* 如何在 FreeBSD 系统中启用 Linux(R) 二进制兼容模式。
* 如何安装额外的 Linux(R) 共享库。
* 如何在 FreeBSD 上安装 Linux(R) 应用程序。
* FreeBSD 上 Linux(R) 兼容模式的实现细节。
在阅读这章之前,您应该知道:
* 知道如何安装 crossref:ports[ports, 额外的第三方软件]。
[[linuxemu-lbc-install]]
== 配置 Linux(R) 二进制兼容模式
默认情况下, Linux(R) 库并没有被安装而且 Linux(R) 二进制兼容模式也没有被启动。 Linux(R) 库可以通过手动安装或者使用 FreeBSD 的 Ports Collection。
安装 package:emulators/linux-base-f10[] 包或者 port 是最容易在 FreeBSD 系统上获得一套基本的 Linux(R) 库的方法。 使用如下方法安装 port:
[source,shell]
....
# cd /usr/ports/emulators/linux_base-f10
# make install distclean
....
安装完成以后, 加载 `linux` 模块启用 Linux(R) 二进制兼容模式:
[source,shell]
....
# kldload linux
....
查看模块是否已经被加载:
[source,shell]
....
% kldstat
Id Refs Address Size Name
1 2 0xc0100000 16bdb8 kernel
7 1 0xc24db000 d000 linux.ko
....
在 [.filename]#/etc/rc.conf# 中加入以下这行后 Linux(R) 兼容模式便会在系统启动时自动开启:
[.programlisting]
....
linux_enable="YES"
....
想要在自制内核中静态链接 Linux(R) 二进制兼容支持的用户可以在自定义的内核配置文件中加入 `options COMPAT_LINUX`。 然后按照 crossref:kernelconfig[kernelconfig,配置FreeBSD的内核] 中所描述的方法编译并安装新内核。
[[linuxemu-libs-manually]]
=== 手动安装额外的库
在配置了 Linux(R) 兼容模式之后, 如果某个 Linux(R) 应用程序依然提示找不到共享库, 需先找出此 Linux(R) 二进制程序需要的共享库再手动安装。
在 Linux(R) 系统上使用 `ldd` 找出应用程序所需的共享库文件。 比如, 在安装有 Doom 的 Linux(R) 系统上运行如下的命令列出 `linuxdoom` 所需用到的共享库文件:
[source,shell]
....
% 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(R) 系统复制到 FreeBSD 上的 [.filename]#/compat/linux#。 复制完成之后, 建立指向第一栏中文件名的符号链接。 这样在 FreeBSD 系统上将会有如下的文件:
[source,shell]
....
/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(R) 共享库文件, 则不再需要复制最后那列文件, 现有的共享库应该可以正常使用。 如果是更新版本的共享库通常建议复制。 只要有符号链接指向新的版本, 那么就可以删除旧版的了。
比如, FreeBSD 系统中现有这些共享库文件:
[source,shell]
....
/compat/linux/lib/libc.so.4.6.27
/compat/linux/lib/libc.so.4 -> libc.so.4.6.27
....
并且 `ldd` 指出某个二进制程序需要之后版本:
[source,shell]
....
libc.so.4 (DLL Jump 4.5pl26) -> libc.so.4.6.29
....
既然现有文件最后的版本号只相差一到两个版本, 程序应该可以正常使用稍旧些的版本。 不管怎样, 使用新版本替换现有 [.filename]#libc.so# 都是安全的。
[source,shell]
....
/compat/linux/lib/libc.so.4.6.29
/compat/linux/lib/libc.so.4 -> libc.so.4.6.29
....
通常最初几次在 FreeBSD 上安装 Linux(R) 程序时需要寻找 Linux(R) 二进制程序所依赖的共享库文件。 在此之后, 系统里便会有足够多的 Linux(R) 共享库文件来运行新安装的 Linux(R) 二进制程序而无需额外操作。
=== 安装 Linux(R) ELF 二进制程序
ELF 二进制程序有时需要额外的步骤。 当未被标记的 ELF 二进制程序被执行的时候, 会生成如下的错误信息:
[source,shell]
....
% ./my-linux-elf-binary
ELF binary type not known
Abort
....
为了帮助 FreeBSD 内核分辨 FreeBSD ELF 二进制程序和 Linux(R) 二进制程序, 请使用 man:brandelf[1]:
[source,shell]
....
% brandelf -t Linux my-linux-elf-binary
....
由于现在的 GNU 工具链能自动把适当的标记信息写入 ELF 二进制程序中,这个步骤通常不是必须做的。
=== 安装基于 Linux(R) RPM 的应用程序
安装基于 Linux(R) RPM 的应用程序, 首先需要安装 package:archivers/rpm[] 包或者 port。 安装好之后 `root` 用户就能使用此命令安装 [.filename]#.rpm# 了:
[source,shell]
....
# cd /compat/linux
# rpm2cpio < /path/to/linux.archive.rpm | cpio -id
....
如有必要的话使用 `brandelf` 标记安装好的 ELF 二进制程序。 注意此项安装将无法干净卸载。
=== 配置主机名解析器
如果 DNS 不能正常工作或是出现以下的错误信息:
[source,shell]
....
resolv+: "bind" is an invalid keyword resolv+:
"hosts" is an invalid keyword
....
请参照此方法配置 [.filename]#/compat/linux/etc/host.conf#:
[.programlisting]
....
order hosts, bind
multi on
....
这里指定了先查询 [.filename]#/etc/hosts# 再查询 DNS。 如果 [.filename]#/compat/linux/etc/host.conf# 不存在的话, Linux(R) 程序便会读取 [.filename]#/etc/host.conf# 并提示与 FreeBSD 的语法不兼容。 如果没有在 [.filename]#/etc/resolv.conf# 文件中配置域名服务器, 可以删除 `bind`。
[[linuxemu-advanced]]
== 高级主题
此章节将讲述是 Linux(R) 二进制兼容如何工作的, 内容基于 Terry Lambert mailto:tlambert@primenet.com[tlambert@primenet.com] (Message ID: `<199906020108.SAA07001@usr09.primenet.com>`) 发表在 {freebsd-chat} 的邮件。
FreeBSD 有一个叫 "execution class loader" 的抽象层。 它被嵌入进了 man:execve[2] 系统调用。
历史上 UNIX(R) 加载器会依靠查看魔数 (通常是文件的开头 4 至 8 个字节)来确认是否是系统已知的的二进制程序, 如果是的话, 就会调用二进制程序加载器。
如果它不是二进制类型的程序, man:execve[2] 调用会返回一个错误, shell 则会把它当作 shell 命令执行。 "不论当前是哪一种 shell" 都会默认做出此种假设。
随后, man:sh[1] 会检查开头的两个字符, 如果它们是 `:\n`, 那么就调用 man:csh[1]。
FreeBSD 有一份加载器列表而不是一个单一的加载器, 并能回退到 `#!` 加载器来运行 shell 解释器或者 shell 脚本。
为了支持 Linux(R) ABI, FreeBSD 看到了二进制 ELF 程序的魔数。 ELF 加载器会查找一个专用的 _标记_, 那是在 ELF 镜像中的一个注释部分, 此区域在 SVR4/Solaris(TM) ELF 二进制中并不存在。
要运行 Linux(R) 二进制程序, 必须先使用 man:brandelf[1] 命令 _标记_ 为 `Linux` 类型:
[source,shell]
....
# brandelf -t Linux file
....
当 ELF 加载器看到了 `Linux` 标记,便会替换 `proc` 结构中的一个指针。 所有的系统调用都通过此指针来索引。 除此以外, 进程被标记以便对 signal trampoline 代码的陷阱向量做特殊处理, 还有一些其他由 Linux(R) 内核模块来处理的(细微)修补。
Linux(R) 系统调用向量包含一个 `sysent[]` 记录的列表, 它的地址位于内核模块之中。
当一个系统调用被 Linux(R) 二进制程序调用时, 陷阱代码会把系统调用函数指针从 `proc` 解引用至 Linux(R) 而不是 FreeBSD 的系统调用入口。
Linux(R) 模式会动态地 _reroots_ 查找。 这与 `union` 文件系统选项是等效的。 首先会试图在 [.filename]#/compat/linux/original-path# 目录查找文件。 如果失败了, 就会在 [.filename]#/original-path# 目录下查找。 这使得需要其它程序的程序得以运行。 例如,Linux(R) 工具链都可以在 Linux(R) ABI 的支持下运行。 也就是说 Linux(R) 二进制程序可以加载并执行 FreeBSD 二进制程序, 如果当前没有相应的 Linux(R) 二进制程序, 可以在 [.filename]#/compat/linux# 目录树中放置一个 man:uname[1] 命令, 使 Linux(R) 程序不易察觉它们并没有运行在 Linux(R) 系统上。
事实上, 在 FreeBSD 内核中有一个 Linux(R) 内核。 所有由内核提供的服务的各种底层功能在 FreeBSD 系统调用表的记录和 Linux(R) 系统调用表的记录是一样的: 文件系统操作, 虚拟内存操作, 信号发送, 和 System V IPC。 唯一的不同是 FreeBSD 会得到 FreeBSD 的 _glue_ 功能, 而 Linux(R) 程序会得到 Linux(R) 的 _glue_ 功能。 FreeBSD 的 _glue_ 功能是静态链接入内核的, 而 Linux(R) 的 _glue_ 功能可以静态链接, 或者通过内核模块访问。
严格说来其实并没有真正的模拟, 这是一种 ABI 的实现。 有时这被称为 "Linux(R) 模拟" 是因为在实现的时候还没有其他适合的词用来描述。 要说 FreeBSD 运行 Linux(R) 二进制程序并不确切, 因为当时代码并还没有被编译进去。
|