distcc (简体中文)
distcc 是一个将 C、C++、Objective C 或 Objective C++ 等程序的编译任务分发到网络中多个主机的程序。distcc 力求实现和本地编译相同的结果,安装、使用都很方便,而且通常比本地编译快很多。distcc 也可以与 Arch 原生的编译工具,比如 makepkg,很好搭配使用。
名词定义
- 客户机
- 启动编译的计算机。
- 志愿机
- 接受客户机发送的编译请求的计算机。一个 distcc 编译集群可以包含一台或多台志愿机。
开始使用
将整个编译集群中的计算机都安装软件包 distcc。如果是其他的发行版,甚至包括使用 Cygwin 的 Windows 操作系统,请阅读 distcc 文档或 distcc(1) 和 distccd(1) 的手册页。请保持 distcc 使用的端口畅通(默认是TCP 3632 端口),参见Category:Firewalls。
配置
工作模式
Distcc 可以在普通模式(默认模式)或泵模式下使用。简单来讲,这两个模式的核心区别在于如何处理预处理过的源代码。普通模式会传递预处理器展开过的源代码和编译选项,由客户机负责预处理,而泵模式会把预处理和编译工作全部分发到 distcc 编译集群中,通常会更快、效率更高。更多细节请参见手册页 man distcc
。
志愿机配置
志愿机的配置文件储存在 /etc/conf.d/distccd
处。最简单的例子,是添加 --allow-private 选项,这样就能够覆盖整个 ipv4 局域网的范围。保存日志到文件也是排错时经常需要的:
DISTCC_ARGS="--allow-private --log-file /tmp/distccd.log"
如果设备有很多网卡,请考虑添加 --listen <监听地址>。其它设置请参考 distccd(1)。
在所有志愿机上启动服务 distccd.service
。要使服务开机自启,请启用此服务。
客户机配置
makepkg 编译选项
编辑/etc/makepkg.conf
的这些部分:
- 确保 BUILDENV 数组中的 distcc 没有被禁用(即前面没有感叹号)
- 取消注释并编辑DISTCC_HOSTS行 ,加入可以使用的志愿机的 IP 地址或主机名。可以加上一个正斜杠("/")以及最大使用线程数。不同 IP 地址用空白隔开,这个清单应该按照处理器性能从高到低排列。
- 修改 MAKEFLAGS 中的 -jN 为所有使用线程数的和的两倍。在下面的示例为 2x(9+5+5+3)=44。
CFLAGS
和 CXXFLAGS
中不能使用 -march=native
选项,否则 distccd 不会将编译任务发给其它机器。另外,没有一个万能配置能够覆盖全部(尤其是线程数与 MAKEFLAGS 的并行线程数)。请一个一个测试您的参数,并与其他参数的结果做比较,以获取最优的配置。下面是一些例子。
普通模式例子
BUILDENV=(distcc fakeroot color !ccache check !sign) MAKEFLAGS="-j44" DISTCC_HOSTS="localhost/9 192.168.10.2/5 192.168.10.3/5 192.168.10.4/3"
泵模式例子
BUILDENV=(distcc fakeroot color !ccache check !sign) MAKEFLAGS="-j70" DISTCC_HOSTS="localhost/9 192.168.10.2,cpp,lzo 192.168.10.3,cpp,lzo 192.168.10.4,cpp,lzo"
请注意以下事项:
- 并行数高的话,泵模式的表现通常更好。
- 在泵模式下,IP 地址或主机名会跟上指示泵模式所需要的",cpp,lzo"的后缀。此处的本机localhost没有加上这个后缀,这意味着,distcc会按照普通模式严格向localhost安排9个任务,并激进地以泵模式向志愿机分发其他任务,这是一个大集群中的通常实践:客户机通常不希望编译任务阻塞他们,以此限制客户机的任务数。如果您一视同仁,也可以加上这个后缀。
- 正如提到的那样,没有一个万能配置能够覆盖全部情况。如何选择一个最优的设置通常需要一些经验。
非 makepkg 编译选项
普通模式例子
下面的最小 distcc 客户机配置包括了志愿者设置,并将他们附加到PATH环境变量上。
$ export PATH="/usr/lib/distcc/bin:$PATH" $ export DISTCC_HOSTS="localhost/9 192.168.10.2/5 192.168.10.3/5 192.168.10.4/3"
泵模式例子
$ export PATH="/usr/lib/distcc/bin:$PATH" $ export DISTCC_HOSTS="localhost/9 192.168.10.2,cpp,lzo 192.168.10.3,cpp,lzo 192.168.10.4,cpp,lzo"
编译
makepkg 编译
普通模式
只要按照上面的过程配置好 /etc/makepkg.conf
就可以了。正常运行 makepkg 即可。
泵模式
用户必须在编译前启动分发泵。因为分发泵pump程序会在启动时检查DISTCC_HOSTS是否正确配置,因此我们需要先定义一个 DISTCC_HOSTS 搪塞过去。makepkg 会使用 /etc/makepkg.conf
里定义的变量,因此不会影响。
$ export DISTCC_HOSTS="localhost,cpp,lzo" $ eval `pump --startup`
然后正常运行 makepkg 即可。
结束时,执行以下指令把分发泵关掉:
$ pump --shutdown
非 makepkg 编译
普通模式
按照#非 makepkg 编译选项章节配置好需要的变量后,直接调用编译器即可:
$ make -j44
有些程序可能需要定义CC 和/或 CXX 变量才能正常工作:
$ make -j44 CC=distcc CXX=distcc
泵模式
按照上面一节方法操作分发泵。其他内容和普通模式相同。
CMake 编译
使用以下 CMake 选项来使用 distcc 编译一个使用 CMake 管理的项目:
$ cmake -DCMAKE_C_COMPILER_LAUNCHER=distcc -DCMAKE_CXX_COMPILER_LAUNCHER=distcc ...
监视进度
软件包 distcc 提供了一个命令行界面的监视器 distccmon-text
来检查编译进度。
如果在调用这个命令行界面监视器加上一个空格和一个等待秒数,就可以按照秒数不断更新显示。
$ distccmon-text 3 29291 Preprocess probe_64.c 192.168.10.2[0] 30954 Compile apic_noop.c 192.168.10.2[0] 30932 Preprocess kfifo.c 192.168.10.2[0] 30919 Compile blk-core.c 192.168.10.2[1] 30969 Compile i915_gem_debug.c 192.168.10.2[3] 30444 Compile block_dev.c 192.168.10.3[1] 30904 Compile compat.c 192.168.10.3[2] 30891 Compile hugetlb.c 192.168.10.3[3] 30458 Compile catalog.c 192.168.10.4[0] 30496 Compile ulpqueue.c 192.168.10.4[2] 30506 Compile alloc.c 192.168.10.4[0]
使用 distcc 交叉编译
交叉编译也可使用 distcc 助力。
- 客户机必须运行在目标架构上。
- 其他架构的志愿机可以协助编译,但必须安装对应的工具链,并配置 distcc。
ARM 架构 Arch Linux 客户机与 x86_64 架构的志愿机
下面一节介绍了如何使用 x86_64 架构的志愿机来帮助 Arch Linux ARM 架构设备编译。只需要一台 x86_64 架构的志愿机就能够加快 2 到 4 倍的编译速度,具体可以参见这些测试。
志愿机
Arch ARM 开发者强烈推荐使用并在 x86_64 架构的志愿机上安装官方的工具链。与其手动配置,AUR提供了所有的工具链、配置文件,和系统服务:
这些工具链的配置过程和#志愿机配置一节完全相似,除了配置文件和系统服务文件对应包名有所更改。比如 armv7h 的配置文件是 /etc/conf.d/distccd-armv7h
,systemd 服务文件是 distccd-armv7h.service
。
注意这些工具链的端口都不同,使得他们可以共存而不冲突。请一定保持他们使用的端口畅通。(参见Category:Firewalls 和手册页 distcc(1))
目标架构 | distcc 端口 |
---|---|
armv5 | 3633 |
armv6h | 3634 |
armv7h | 3635 |
armv8h/aarch64 | 3636 |
客户机
配置 Arch ARM 客户机的最简单办法是使用distccd-arch-armAUR包。这个包提供了针对Arch ARM架构的所有四个配置文件和系统服务配置文件。比如说如果运行的是armv7h固件,只需要配置 /etc/conf.d/distccd-armv7h
文件并按照上述内容编辑默认值。再启动 distccd-armv7h.service
服务以编译。
更详细的介绍,请参见使用示例。
如果希望不使用AUR包而手动配置客户机,可以按照通用的方法配置客户机,但需要更改如下两个内容来更改默认端口。例如(端口假设是按照上面的表格):
-
/etc/conf.d/distcc
: armv7h 示例:DISTCC_ARGS="--allow-private --log-level info --log-file /tmp/distccd-armv7h.log --port 3635"
-
/etc/makepkg.conf
: armv7h 示例:DISTCC_HOSTS="192.168.10.2/5:3635 192.168.10.3/5:3635"
x86_64 架构的 Arch Linux 客户机与 ARM 架构志愿机
下面一节介绍了如何使用 ARM 架构的志愿机来帮助x86_64 架构客户机编译。只需要一台 x86_64 架构的志愿机就能够显著加快编译速度,两台设备就能翻倍。具体可以参见这些测试。
客户机
可以按照通用的方法配置客户机,其对应的标准端口是 3632。
志愿机
distccd-x86_64AUR 提供了装在 Arch ARM 设备上的交叉编译工具。
附加工具链
- EmbToolkit: 创建交叉编译工具链的工具;支持 ARM 和 MIPS 架构;支持创建LLVM工具链
- crosstool-ng:和EmbToolkit相似,支持更多架构(更多信息参见其网站)
- Linaro:提供了ARM开发的工具链
EmbToolkit
有一个很好看的配置工具链的图形配置菜单(make xconfig
)。
故障排除
编译 Arch Linux 内核包时的奇怪现象
如果使用官方的PKGBUILD来编译内核,distcc 将无法工作,因为内核硬编码使用了一些GCC插件,因为一些技术原因distccd不能够支持他们。
可以通过编辑内核、删掉这些对 GCC 插件的要求来迂回。可以在编译前在 PKGBUILD 中用一行 sed 命令搞定:
sed -i '/HAVE_GCC_PLUGINS/d' arch/x86/Kconfig
如果不这样就无法编译。参见 FS#64275。
另一种办法是编译时传递 CC=distcc 和 CXX=distcc 变量:
make all CC=distcc CXX=distcc
编译 chromium 包时的奇怪现象
编译 chromium 会使用 clang,目前受到issue#386的影响。规避这个bug可以在PKGBUILD的_flags
数组中添加这些:
'is_cfi=false' 'use_gold=false' 'clang_use_default_sample_profile=false' 'chrome_pgo_phase=0'
Journalctl
使用 journalctl
查看什么地方出错了:
# journalctl $(which distccd) -e --since "5 min ago"
调整日志记录等级
通常来说,distcc会把log写到 /var/log/messages.log
里。一个技巧(这个是distccd的手册页推荐的)是写到其他文件里面,比如写到内存(通过 /tmp)里去。另一个技巧是提高日志记录等级,只记录错误信息。这个等级可以是任何标准系统日志等级,即critical、error、warning、notice、info,或者debug。
在客户机上按照这些参数启动distcc,或修改志愿机的/etc/conf.d/distccd
文件的DISTCC_ARGS参数:
DISTCC_ARGS="--allow 192.168.10.0/24 --log-level error --log-file /tmp/distccd.log"
修改 $HOME/.distcc 文件夹位置以减少硬盘读写
distcc 默认会创建 $HOME/.distcc
文件夹来暂时保存节点编译相关的信息。下面的命令可以减少不必要的硬盘读写,尤其对 SSD 友好:
$ export DISTCC_DIR=/tmp/distcc
distccd-alarm 相关
没有那个文件或目录
与下例类似的错误表明用户错误地启动了 distcc 而不是 distccd-alarm 包(即distccd-alarm-armv5AUR、distccd-alarm-armv6hAUR、distccd-alarm-armv7hAUR,或 distccd-alarm-armv8AUR)提供的distccd。
请一定针对目标架构启动正确的服务。
distcc[25479] (dcc_execvp) ERROR: failed to exec armv7l-unknown-linux-gnueabihf-g++: No such file or directory
另请参见
- icecreamAUR - 一个更好配置的 distcc 分支。