Ubuntu源码编译卡在checking?配置探测超时排查与提速指南
1. 编译卡在 checking 的本质不是慢是配置阶段在反复试探很多人第一次在 Ubuntu 上从源码装软件都会遇到同一个画面敲完./configure或者直接make终端刷出一行checking for xxx...然后光标停在那里一分钟、五分钟、十分钟都不动。新手第一反应是编译好慢于是去换 CPU、加内存、上 SSD结果发现该卡还是卡。这里必须先纠正一个认知卡在 checking 阶段绝大多数时候根本不是编译慢而是配置脚本在做环境探测而探测过程被某个环节拖住了。要理解这件事得先搞清楚源码安装的标准三步./configure、make、make install。configure是一个由 Autoconf 生成的 shell 脚本它的工作是在真正编译之前把当前系统的编译器、头文件、库文件、系统调用、工具链版本全部摸一遍然后生成Makefile。那些checking for...就是它在逐个试探这个函数存在吗这个头文件在哪这个库能不能链接每一条 checking 背后往往是一次真实的编译加链接测试。问题就出在这里。这些测试默认是串行执行的而且很多测试会去访问网络、扫描大目录、调用外部命令。一旦某个测试超时或者反复重试整个流程就停在那条 checking 上。你看到的慢其实是它在等一个超时或者在反复尝试一个注定失败的探测。所以解决思路不是让编译更快而是让配置阶段的探测不再无谓地等待和重试。我在一台老笔记本上装某个图像处理库时configure卡在checking for gcc...足足两分钟当时以为死机了后来才发现是脚本在多个候选编译器之间逐个试。这种场景太典型了下面我把常见原因和对应解法拆开讲你可以对着自己的终端逐条排查。提示判断到底卡在哪一步最直接的办法是看当前那行 checking 的完整文字。它写的是什么问题基本就在那个方向。别急着 CtrlC先让它多跑一会儿记录下卡住的具体条目。2. 先分清是真卡死还是在等超时三种典型现象在动手改配置之前得先判断你遇到的是哪一类问题。同样是停在 checking背后的成因完全不同处理方式也南辕北辙。我把它归成三类你可以对号入座。2.1 现象一某条 checking 停留很久然后自己过去了这种最常见也最容易被误判。表现是某一行checking for xxx...停了一两分钟然后突然打印出yes或no继续往下走。这说明脚本在做超时等待通常是网络探测或者 DNS 解析。比如某些构建系统会去检查某个远程资源是否可达或者尝试解析一个主机名。网络不通时它不会立刻失败而是要等系统默认的超时时间通常是几十秒到几分钟。这类问题的特征是它最终会过去只是慢。如果你有耐心整个 configure 跑完可能也就多花十几分钟。但如果你反复重装、反复 configure这个时间会被无限放大。解决办法是切断这些网络探测或者给系统设置更短的超时。2.2 现象二某条 checking 永远停住CPU 占用很低这种才是真正的卡死。表现是终端那行 checking 再也不动了同时你开另一个终端看top发现 configure 进程的 CPU 占用接近 0。这说明它在等一个永远不会返回的东西可能是锁、可能是某个设备、也可能是子进程僵死。我遇到过最典型的一次是脚本在探测某个串口设备或者某个系统服务而那个服务在当前环境里根本没启动探测调用就挂在那里。这种情况你等再久也没用必须定位到具体是哪条 checking然后针对性跳过。2.3 现象三checking 飞快刷过但 make 阶段慢得离谱还有一种情况容易被混淆configure 阶段其实很快就过了真正慢的是后面的make。这时候终端刷的不是 checking而是CC、CXX、LD这些编译链接命令。如果你把这两种情况搞混就会用错药。区分方法很简单看终端最后一行是什么。是checking for...就是配置阶段的问题是CC xxx.o或者gcc -c ...就是编译阶段的问题。编译阶段慢通常是并行度不够、优化等级太高、或者单核性能瓶颈处理方式和配置阶段完全不同。本文重点讲配置阶段的 checking 卡顿编译阶段的优化我会在最后一节顺带提一下。现象终端特征CPU 占用本质原因处理方向等超时checking 停留后自行通过低但非零网络/DNS 探测超时切断网络探测、缩短超时真卡死checking 永久停住接近 0等待锁/设备/僵死子进程定位条目并跳过编译慢刷 CC/LD 命令高并行度低、优化等级高加 -j、调优化参数把这三类分清楚你就不会再把配置探测慢当成编译慢来治了。接下来进入具体排查。3. 定位卡点用 strace 和 configure 日志把问题钉死光靠肉眼看终端很多时候只能知道卡在 checking for xxx但不知道它到底在等什么。这时候需要上工具把进程的系统调用扒出来看。这是我从无数次排查里总结出的最有效手段比瞎猜强一百倍。3.1 用 strace 看进程到底卡在哪个系统调用strace能跟踪一个进程的所有系统调用。当 configure 卡住时你在另一个终端找到它的 PID然后 attach 上去# 先找到 configure 或 make 的进程号 ps aux | grep -E configure|make|cc1 # 假设 PID 是 12345attach 上去看它在干什么 sudo strace -p 12345 -f -tt -s 200参数含义-p指定进程-f跟踪子进程-tt打印精确时间戳-s 200把字符串参数打印到 200 字符。attach 之后你会看到它当前阻塞在哪个调用上。常见的几种结果卡在connect()或poll()说明在等网络连接典型的超时问题。卡在read()某个文件描述符可能在等某个设备或管道的数据。卡在wait4()在等子进程结束而子进程可能已经僵死。卡在flock()或fcntl()在等文件锁通常是并发 configure 导致的。看到具体调用问题就清晰了。比如卡在connect()到某个外部地址那基本可以确定是网络探测直接按下一节的方法切断即可。3.2 翻 config.logconfigure 自己记的账本Autoconf 生成的 configure 脚本会把所有探测结果写进当前目录的config.log。这个文件是排查的金矿很多人却从来不看。当 configure 卡住或者失败时第一件事就是去看它# 看最后 100 行通常卡点就在末尾 tail -n 100 config.log # 搜索失败的探测 grep -n failed\|error\|cannot\|no such config.log | head -50config.log里会记录每条 checking 对应的实际测试命令和输出。如果某条探测超时你能看到它执行的完整命令甚至能看到超时时间。我排查过一个案例config.log 里显示脚本在反复调用一个外部工具去查询版本而那个工具每次都要启动一个重量级运行时启动一次好几秒几十次下来就是几分钟。这种问题看终端根本看不出来一看日志就明白了。3.3 给 configure 加 --verbose 和超时保护有些 configure 支持--verbose会把每条探测的细节都打出来。另外可以用timeout命令给整个 configure 加一个上限防止它无限卡# 加详细输出同时设置 10 分钟总超时 timeout 600 ./configure --verbose 21 | tee configure_run.log这样即使卡住10 分钟后也会被强制结束而且tee把完整输出存了下来方便事后分析卡在哪一条。这个习惯我强烈建议养成尤其是第一次编译某个陌生项目时别裸跑 configure。注意timeout结束进程时configure 可能留下半成品文件重新跑之前最好make distclean清理一下避免状态污染。4. 对症下药让 checking 不再无谓等待的六种实操手段定位到卡点之后就可以动手了。下面这六种手段覆盖了我遇到过的绝大多数 checking 卡顿场景按从简单到复杂的顺序排列你可以逐个尝试。4.1 切断网络探测给 configure 断网或改 hosts如果 strace 显示卡在connect()最直接的办法就是让这些网络探测快速失败。有两种做法第一种临时断网跑 configure。把网线拔了或者禁用网络接口这样所有网络连接会立刻返回网络不可达而不是傻等超时。配置阶段本来就不该依赖网络断网跑反而更干净。# 查看网络接口 ip link show # 临时禁用把 eth0 换成你的接口名 sudo ip link set eth0 down # 跑完 configure 再启用 sudo ip link set eth0 up第二种如果不想断网可以改/etc/hosts把脚本要探测的域名指向本地回环让它秒失败# 编辑 hosts把可疑域名指到 127.0.0.1 sudo vim /etc/hosts # 加入一行例如 127.0.0.1 example-probe.invalid这样 DNS 解析瞬间返回连接也瞬间被拒探测立刻结束。改完记得跑完 configure 后清理掉别长期留着。4.2 缩短系统超时让 TCP 连接快速失败有些探测走的是 TCP即使域名解析了连接也可能因为对端不响应而等很久。Linux 的 TCP 连接超时默认是几十秒到几分钟可以通过内核参数临时调短# 查看当前 TCP 重试次数 cat /proc/sys/net/ipv4/tcp_syn_retries # 临时改成 1让连接快速失败 sudo sysctl -w net.ipv4.tcp_syn_retries1tcp_syn_retries默认是 6意味着 SYN 包会重试 6 次总时间接近两分钟。改成 1 之后几秒内就会失败返回。这个改动是临时的重启后失效跑完 configure 想恢复就把它设回 6。这个技巧在处理卡在 connect的场景里特别管用我几乎每次都会用。4.3 跳过特定探测用缓存变量直接喂答案Autoconf 支持通过环境变量或者--cache-file直接告诉它探测结果跳过实际测试。这是最精准的做法。比如你知道某个函数在当前系统上一定存在就可以直接赋值# 直接告诉 configure 某个检查的结果跳过实际探测 ./configure ac_cv_func_xxxyes ac_cv_header_yyy_hyes变量名的规律是ac_cv_func_函数名、ac_cv_header_头文件名、ac_cv_lib_库名_函数名。具体卡在哪条 checking就去 config.log 里找对应的变量名然后手动赋值。这样脚本就不会再去实际编译测试直接采用你给的结果。更系统的做法是用缓存文件。第一次跑 configure 时加-C生成缓存之后重跑就会复用# 第一次生成缓存 ./configure -C # 之后重跑直接读缓存跳过大量探测 ./configure -C缓存文件是config.cache你可以手动编辑它把那些慢的探测结果写死。这个办法在反复 configure 调试时能省下大量时间。4.4 并行化配置能并行的探测别串行部分现代构建系统支持并行配置。比如 CMake 的配置阶段本身较快而 Autoconf 的 configure 是纯串行的没法直接并行。但如果你用的是基于 CMake 的项目可以开启并行# CMake 配置阶段尽量利用多核 cmake -S . -B build -G Unix Makefiles # 编译阶段并行 cmake --build build -j$(nproc)对于 Autoconf 项目虽然 configure 本身不能并行但你可以把那些耗时的探测提前用缓存固定下来效果类似。另外如果项目同时提供 CMake 和 Autoconf 两套构建优先用 CMake配置阶段通常快很多。4.5 换用更快的 shell 和关闭调试输出configure 是 shell 脚本shell 的执行效率会影响整体速度。Ubuntu 默认的/bin/sh是 dash比 bash 快但有些脚本强制用 bash。你可以检查一下# 看 /bin/sh 指向谁 ls -l /bin/sh # 如果是 dash通常已经是最快的选择另外如果 configure 带了大量set -x之类的调试输出终端 IO 会成为瓶颈。把输出重定向到文件而不是直接刷屏能明显加快./configure configure_output.log 21别小看这一点终端渲染几万行输出本身就要花不少时间重定向到文件后脚本跑起来会顺畅很多。4.6 升级工具链老版本 Autoconf 的探测逻辑更笨有些卡顿纯粹是工具链太老导致的。老版本的 Autoconf 生成的 configure 脚本探测逻辑更保守重试更多。检查一下版本autoconf --version automake --version libtool --version如果版本很老可以考虑升级。但要注意升级 Autoconf 后需要重新生成 configure 脚本也就是跑autoreconf -i。这一步本身也可能慢但生成出来的新脚本探测效率更高。这个手段属于治本适合长期维护的项目。手段适用场景操作成本效果断网/改 hosts网络探测超时低立竿见影调 tcp_syn_retriesTCP 连接等待低快速失败缓存变量特定探测慢中精准跳过并行配置CMake 类项目低明显加速重定向输出输出刷屏严重低小幅加速升级工具链老项目长期维护高治本5. 编译阶段才是真慢那得从并行度和优化等级下手前面讲的都是配置阶段的 checking 卡顿。但如果你确认卡的是make阶段终端刷的是CC、CXX那问题性质就变了。这一节专门讲编译阶段的提速因为很多人把这两件事混为一谈。5.1 并行编译-j 参数到底该给多少make默认是单线程的一个文件一个文件编译。现代机器都是多核不加-j就是浪费。正确用法# 用所有核心 make -j$(nproc) # 保守一点留一个核心给系统 make -j$(($(nproc)-1))nproc会输出当前可用的 CPU 核心数。但-j不是越大越好。如果每个编译单元内存占用很高核心数开太多会导致内存耗尽触发 OOM反而更慢甚至失败。经验法则是内存充足时用满核心内存紧张时按内存算。粗略估算每个编译进程按 1 到 2 GB 内存预留比如 8 GB 内存的机器-j4比较稳妥。我踩过一次坑在一台 4 GB 内存的虚拟机上make -j8结果编译到一半系统开始疯狂 swap最后 OOM 把编译器杀了。后来改成-j2虽然慢一点但稳定跑完。所以-j的值要结合内存来定别盲目拉满。5.2 优化等级-O2 和 -O3 的取舍编译慢的另一个大头是优化等级。-O3会做大量激进优化编译时间可能是-O2的两三倍而性能提升往往只有几个百分点。如果你只是自己用不追求极致性能把优化等级降到-O2甚至-O1能省下大量时间# 通过 CFLAGS 覆盖默认优化等级 ./configure CFLAGS-O2 CXXFLAGS-O2 # 或者编译时指定 make CFLAGS-O2 CXXFLAGS-O2注意有些项目在 configure 阶段就把优化等级写进了 Makefile这时候你需要改 Makefile 或者用环境变量覆盖。另外调试版本通常用-O0 -g编译最快但运行慢适合开发阶段。5.3 ccache重复编译的救星如果你需要反复编译同一个项目比如改一点代码就重编那ccache是必装的。它把编译结果缓存起来下次遇到相同的源文件和编译参数直接返回缓存跳过实际编译# 安装 sudo apt install ccache # 让编译器走 ccache export CCccache gcc export CXXccache g # 查看缓存命中情况 ccache -s第一次编译还是慢但从第二次开始只要源文件没变编译几乎是瞬间完成。对于大型项目ccache 能把重复编译时间砍掉一大半。这个工具我强烈推荐属于装了就回不去的那种。5.4 分布式编译多机协作的进阶玩法如果项目特别大单机多核也不够用可以考虑分布式编译工具把编译任务分发到多台机器上。这类工具配置复杂适合团队或者有闲置机器的场景。个人开发者一般用不上但知道有这条路需要的时候能想起来。提示分布式编译对网络和机器一致性要求高配置不当反而更慢。先用好-j和 ccache这两招能解决 90% 的编译慢问题。6. 几个我踩过的坑和对应的土办法理论讲完了分享几个真实踩过的坑都是文档里不会写、但实际会遇到的。6.1 configure 卡在检查 Python 或 Perl 模块有些项目在 configure 阶段会去检查 Python 或 Perl 模块是否存在。如果系统里装了多个版本脚本可能逐个试探每个都要启动一次解释器慢得离谱。更坑的是如果某个模块检查会触发网络请求比如去包索引查询那就直接卡死。土办法是提前把不需要的解释器路径从 PATH 里摘掉或者用--with-python之类的参数直接指定版本避免它到处找。我遇到过一次脚本在检查 Perl 的某个模块时因为模块的加载脚本里有网络调用卡了整整五分钟。后来直接把那个 Perl 从 PATH 里临时移除秒过。6.2 文件系统是网络挂载时checking 会奇慢如果你的源码放在 NFS 或者 SMB 挂载的目录里configure 阶段的大量文件读写会变得极慢。每条 checking 都要 stat 一堆文件网络文件系统的延迟被放大几十倍。解决办法很简单把源码复制到本地磁盘再编译。这个坑我踩过同样的项目网络盘上 configure 要十几分钟本地盘两分钟搞定。6.3 杀毒或安全软件拦截了编译测试有些环境装了实时文件监控或安全软件configure 阶段每次编译测试生成临时可执行文件都会被扫描一遍导致每条 checking 都慢。这种情况在受管控的机器上常见。土办法是把编译目录加入白名单或者临时关闭实时监控。当然这要看你的环境策略别违规操作。6.4 并发跑多个 configure 导致锁竞争有时候你为了省时间同时开几个终端跑不同项目的 configure。如果它们共享某些缓存目录或者临时目录可能产生文件锁竞争互相拖慢。表现就是每个都卡在某个 checking 上不动。解决办法是串行跑或者给每个 configure 指定独立的临时目录# 给 configure 指定独立的 TMPDIR TMPDIR/tmp/build1 ./configure这个细节很隐蔽但确实会导致莫名其妙的卡顿。7. 一套可复用的排查流程最后把我平时排查这类问题的流程整理成一套可复用的步骤你下次遇到直接照着走。第一步看终端最后一行。是checking for...还是CC/LD前者是配置阶段后者是编译阶段方向完全不同。第二步如果是配置阶段先等两分钟。有些探测就是慢别急着下结论。同时开另一个终端top看 CPU 占用接近 0 说明在等 IO 或网络。第三步用strace -p PID -f -ttattach 上去看卡在哪个系统调用。connect是网络read是文件或设备wait4是子进程flock是锁。第四步翻config.log最后 100 行找到卡住那条 checking 对应的实际命令和变量名。第五步对症处理网络问题就断网或调tcp_syn_retries特定探测慢就用缓存变量跳过文件系统慢就换本地盘。第六步如果确认是编译阶段慢加-j、降优化等级、上 ccache。第七步把这次的处理方式记下来下次遇到同类项目直接复用。我习惯在项目根目录留一个build_notes.md记录这个项目在什么环境下需要哪些特殊参数省得每次重新踩坑。这套流程看起来步骤多但熟练之后从发现问题到定位原因通常五分钟内就能搞定。关键是要养成先定位再动手的习惯别一上来就瞎改配置那样只会把问题搞得更复杂。注意所有临时改动比如改 hosts、调内核参数、改 PATH跑完记得恢复。我见过有人改完tcp_syn_retries忘了改回来结果后面所有网络操作都变得异常敏感排查了半天才想起来是这里的问题。源码安装这件事配置阶段的 checking 卡顿是最劝退新手的环节但一旦理解了它的工作原理处理起来其实很有章法。核心就一句话checking 慢不是编译慢是探测在等超时或反复重试找到它在等什么切断它问题就解决了。编译阶段的提速则是另一套逻辑并行度和缓存是关键。把这两件事分开对待你在 Ubuntu 上从源码装软件会顺畅得多。