☰
Ubuntu源码编译卡在checking?定位与解决网络阻塞问题
2026/10/21 3:44:01 网站建设 项目流程

1. 编译卡在 checking 的本质:不是慢,是卡住了

如果你在 Ubuntu 上从源码编译过软件,大概率见过这个场景:敲完./configure,终端刷出一行checking for gcc...,然后光标就停在那里,一分钟、五分钟、十分钟纹丝不动。你以为是机器性能不行,换了台配置更高的机器,结果还是一样。问题根本不在 CPU 算力,而在于配置脚本正在等待一个永远不会返回结果的网络请求。

这个现象在源码编译圈子里极其常见,尤其是编译那些依赖 autotools 构建系统的项目时。configure脚本在正式生成 Makefile 之前,会做大量环境探测工作:检查编译器是否存在、检查某个头文件能不能用、检查某个库的版本号够不够。这些检查绝大多数是本地操作,速度很快。但有一类检查会触发网络访问,一旦网络环境不通畅,脚本就会一直阻塞在超时等待上,表现出来就是"一直在 checking"。

我最早踩这个坑是在一台内网机器上编译一个图像处理库。那台机器没有外网访问权限,./configure跑了四十分钟还没结束,用strace跟了一下才发现,进程卡在一个connect()系统调用上,目标地址是某个外部主机。从那以后我就养成了一个习惯:编译任何源码之前,先看一眼 configure 脚本里有没有网络探测逻辑。

这篇文章适合所有在 Ubuntu 上从源码编译软件的人,不管你是刚接触 Linux 编译的新手,还是已经编译过几十个项目的老手。我会把"为什么卡"和"怎么解决"这两件事讲透,给出可以直接复制粘贴的操作步骤,也会分享一些我这些年积累的排查技巧和避坑经验。

2. 编译流程拆解:configure 阶段到底在干什么

2.1 从源码到可执行文件的三步走

在解决问题之前,有必要把源码编译的完整流程理清楚。Ubuntu 下从源码安装软件,标准流程是三步:

  1. ./configure:探测当前系统的环境,生成适配本机的 Makefile
  2. make:根据 Makefile 里的规则,调用编译器把源码编译成目标文件,再链接成可执行文件
  3. make install:把编译好的文件复制到系统指定目录

很多人以为"编译慢"是第二步make的问题,但实际上,卡在 checking 的现象几乎全部发生在第一步./configure阶段。因为make阶段做的是纯粹的编译和链接工作,不涉及网络请求。而configure阶段要做环境探测,某些探测逻辑会尝试访问网络。

这里有个容易混淆的点:用户描述的"make 时一直在 checking",严格来说不准确。make命令本身不会输出checking字样,那些checking for xxx...的输出全部来自configure脚本。所以当你看到终端在刷checking,说明你还在 configure 阶段,或者你用的是一条把 configure 和 make 串起来的命令(比如./configure && make),实际卡住的是前半段。

2.2 configure 脚本的网络探测逻辑

configure脚本由 autoconf 工具生成,本质上是一个超长的 shell 脚本。它在探测环境时,会执行大量的测试用例。大部分测试是本地操作,比如:

  • 检查gcc能不能编译一个最简单的 C 程序
  • 检查stdio.h头文件是否存在
  • 检查某个函数的签名是否匹配

但有一类测试会触发网络访问,常见的有这几种:

第一种是检查依赖库的在线版本。某些项目的 configure 脚本会尝试连接软件包仓库,确认依赖库的版本是否满足要求。这种设计在开发者本机可能没问题,但在内网环境或者网络受限的环境下就会卡住。

第二种是下载辅助脚本或数据文件。一些项目在 configure 阶段需要下载额外的构建工具或配置文件,如果下载源不可达,脚本就会一直等待超时。

第三种是 DNS 解析阻塞。即使 configure 脚本本身没有显式的网络请求,它调用的某些工具(比如某些版本的 pkg-config)可能会触发 DNS 查询。如果 DNS 服务器配置不当,每次查询都要等好几秒才超时,累积起来就非常可观。

2.3 为什么"等一等"解决不了问题

很多人遇到 checking 卡住,第一反应是"再等等,可能机器慢"。这个思路在大多数情况下是错的。本地编译检查再慢,也就是毫秒到秒级别的差异。如果你等了一分钟以上还没有任何新输出,那基本可以确定是网络阻塞,而不是计算慢。

网络阻塞的特点是:进程处于不可中断的等待状态,不会消耗 CPU,也不会产生新的输出。你可以用top命令观察,如果编译进程的 CPU 占用率接近 0%,同时终端没有任何新行输出,那几乎可以确定是卡在网络请求上了。

注意:有些 configure 脚本设置了很长的超时时间,比如 120 秒甚至 300 秒。这意味着你可能要等好几分钟才能看到它跳过这个检查。与其干等,不如主动干预。

3. 定位卡点的实操方法:别猜,用工具看

3.1 用 strace 精准定位阻塞位置

遇到 checking 卡住,最有效的排查工具是strace。它能跟踪进程的所有系统调用,让你清楚看到进程到底卡在哪个操作上。

操作步骤很简单。首先找到卡住的进程 PID:

ps aux | grep configure

或者如果你是在另一个终端里操作,可以直接用:

pgrep -f configure

拿到 PID 之后,用 strace 附加到该进程上:

sudo strace -p <PID> -f -e trace=network

参数说明:-p指定进程号,-f跟踪子进程,-e trace=network只显示网络相关的系统调用。如果进程卡在网络请求上,你会看到类似这样的输出:

connect(3, {sa_family=AF_INET, sin_port=htons(80), sin_addr=inet_addr("xxx.xxx.xxx.xxx")}, 16) = -1 EINPROGRESS (Operation now in progress)

这就说明进程正在尝试连接某个外部主机的 80 端口,但连接没有成功返回。看到这个输出,问题就确认了:configure 脚本在等一个网络连接,而这个连接在当前环境下无法建立。

如果你没有安装 strace,可以先装一下:

sudo apt install strace

3.2 用 ltrace 查看库函数调用

strace 看的是系统调用层面,如果你想看更上层的信息,比如进程在调用哪个库函数,可以用ltrace:

sudo ltrace -p <PID>

ltrace 能显示进程调用的动态库函数,比如getaddrinfo、connect等。如果看到进程反复调用getaddrinfo并且每次都返回失败,那说明是 DNS 解析出了问题。

3.3 用 timeout 命令给 configure 加超时保护

与其等 configure 自己超时,不如主动给它加一个时间限制。timeout命令可以做到这一点:

timeout 60 ./configure --prefix=/usr/local

这条命令的意思是:给 configure 最多 60 秒的执行时间,超过就强制终止。这样你就不用干等着了。如果 60 秒内 configure 没有完成,说明确实有阻塞,你可以根据前面 strace 的结果来针对性处理。

3.4 查看 configure 脚本中的网络相关代码

如果你想知道 configure 脚本里到底哪些地方会触发网络请求,可以直接搜索脚本内容:

grep -n "wget\|curl\|http\|ftp\|download" configure

这条命令会列出 configure 脚本中所有涉及网络操作的代码行。你可以根据行号定位到具体的检查逻辑,判断这个检查是否必要。

实操心得:我一般会先用grep快速扫一遍 configure 脚本,看看有没有明显的网络请求。如果有,再决定是跳过这个检查还是配置代理。这个方法比 strace 更快,适合在编译之前就做好预判。

4. 六种解决方案:从临时绕过到彻底修复

4.1 方案一:跳过网络检查(最直接)

如果 configure 脚本中的网络检查不是必须的,最直接的办法就是跳过它。具体怎么跳过,取决于脚本的实现方式。

方法 A:设置环境变量。很多 configure 脚本会检查某个环境变量来决定是否执行网络请求。比如有些脚本会检查http_proxy变量,如果设置了就尝试通过代理访问,如果没设置就跳过。你可以尝试:

./configure --prefix=/usr/local --disable-download

或者:

NO_NETWORK=1 ./configure --prefix=/usr/local

具体用哪个变量名,需要看 configure 脚本的说明。你可以用./configure --help查看所有可用选项,找到和网络相关的开关。

方法 B:直接修改 configure 脚本。如果脚本没有提供跳过选项,你可以手动注释掉网络检查相关的代码。用文本编辑器打开 configure,找到触发网络请求的那几行,在前面加#注释掉。修改之前建议先备份:

cp configure configure.bak

然后用vim或nano编辑。找到类似这样的代码块:

# 原始代码 if test "$cross_compiling" = no; then $as_echo "$as_me: checking for online resources" >&6 wget -q http://example.com/resource -O resource.txt fi

把它改成:

# 修改后 if test "$cross_compiling" = no; then $as_echo "$as_me: skipping online resource check" >&6 # wget -q http://example.com/resource -O resource.txt fi

这样脚本就会跳过网络请求,直接继续后面的检查。

注意:修改 configure 脚本后,如果后续执行了autoreconf或autoconf命令,修改会被覆盖。所以修改后直接运行./configure和make,不要再重新生成 configure 脚本。

4.2 方案二:配置本地缓存或离线资源

如果网络检查是必须的,但当前环境无法访问外网,可以考虑提前把需要的资源下载到本地,然后让 configure 脚本从本地读取。

步骤一:在能上网的机器上下载所需资源。根据 configure 脚本的提示,确定它需要下载什么文件。通常是一个压缩包或者一个数据文件。

步骤二:把文件放到指定位置。把下载好的文件复制到源码目录下,或者复制到 configure 脚本期望的路径。

步骤三:修改 configure 脚本指向本地文件。把脚本中的下载地址改成file://协议的本地路径:

# 原始代码 wget -q http://example.com/data.tar.gz -O data.tar.gz # 修改后 cp /path/to/local/data.tar.gz ./data.tar.gz

这样脚本就不需要联网了。

4.3 方案三:设置合理的超时时间

如果网络检查无法跳过,但你知道它最终会超时并继续,那可以缩短超时时间,减少等待。

很多网络工具都支持超时参数。比如wget可以用--timeout指定超时秒数,curl可以用--connect-timeout和--max-time。如果 configure 脚本里用的是这些工具,你可以通过环境变量或者修改脚本的方式缩短超时。

以 wget 为例,可以在脚本中找到 wget 调用,加上超时参数:

# 原始代码 wget -q http://example.com/resource -O resource.txt # 修改后 wget -q --timeout=5 --tries=1 http://example.com/resource -O resource.txt

这样即使网络不通,5 秒后就会放弃,不会一直卡着。

4.4 方案四:使用系统包管理器替代源码编译

如果你编译这个软件的目的只是使用它,而不是修改它的源码,那最省事的方案是用 Ubuntu 的包管理器直接安装:

sudo apt install <package-name>

Ubuntu 的官方仓库里有大量常用软件,安装速度快,依赖自动处理,不需要手动编译。你可以先用apt search查一下有没有现成的包:

apt search <keyword>

如果仓库里有,直接安装就行,完全绕过了编译环节。

实操心得:我见过很多人明明可以用apt install一行命令搞定的事情,非要花几个小时从源码编译。除非你需要特定版本、特定编译选项,或者需要修改源码,否则优先用包管理器。

4.5 方案五:换用国内镜像源加速下载

如果 configure 阶段的网络请求是下载依赖包,而下载速度慢是因为源站在国外,那可以换成国内镜像源。

以常见的软件源为例,Ubuntu 本身的 apt 源可以换成国内镜像:

sudo sed -i 's/archive.ubuntu.com/mirrors.aliyun.com/g' /etc/apt/sources.list sudo apt update

对于 configure 脚本中调用的下载工具,如果它支持指定镜像地址,也可以通过环境变量或配置文件来切换。比如 pip 可以配置国内源:

pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple

这样后续的下载就会走国内镜像,速度快很多。

4.6 方案六:使用 ccache 加速重复编译

虽然 ccache 不能解决 checking 卡住的问题,但它能显著加速make阶段的编译速度。如果你需要反复编译同一个项目(比如调试代码时),ccache 可以把编译结果缓存起来,下次编译时直接复用。

安装 ccache:

sudo apt install ccache

然后在 configure 时指定使用 ccache:

./configure CC="ccache gcc" CXX="ccache g++"

或者在~/.bashrc里添加:

export CC="ccache gcc" export CXX="ccache g++"

这样每次编译时,ccache 会先检查缓存,如果之前编译过相同的代码,就直接返回缓存结果,省去重复编译的时间。

5. 常见问题速查与排查技巧

5.1 常见问题对照表

现象可能原因排查方法解决方案
configure 卡在 checking for gcc编译器未安装或路径不对which gcc检查sudo apt install build-essential
configure 卡在 checking for xxx library依赖库缺失`dpkg -lgrep xxx`
configure 卡在 checking for online resources网络请求阻塞strace -p PID -e trace=network跳过检查或配置本地资源
configure 卡在 checking whether to build xxx脚本逻辑死循环查看 configure 脚本对应行修改脚本或换版本
make 阶段报错找不到头文件开发包未安装查看报错信息中的头文件名apt install libxxx-dev
make 阶段编译极慢单线程编译nproc查看核心数make -j$(nproc)
make install 权限不足未使用 sudo查看报错信息sudo make install

5.2 排查思路:从外到内逐层定位

遇到编译问题,我习惯按这个顺序排查:

第一层:确认基础工具链是否完整。运行gcc --version、make --version、pkg-config --version,确保这些基础工具都装了。Ubuntu 上一条命令搞定:

sudo apt install build-essential pkg-config

第二层:确认依赖库是否齐全。查看项目文档中的依赖列表,逐个确认是否安装。可以用dpkg -l检查已安装的包,用apt-cache search查找可用的包。

第三层:确认网络环境是否正常。如果前面两层都没问题,但 configure 还是卡住,那大概率是网络问题。用 strace 确认阻塞位置,然后按第 4 节的方案处理。

第四层:确认 configure 脚本本身是否有 bug。极少数情况下,configure 脚本本身存在逻辑错误,导致在某些环境下死循环。这种情况可以尝试换一个版本的源码,或者去项目的 issue 列表里搜索是否有类似问题。

5.3 独家避坑技巧

技巧一:编译前先读 README 和 INSTALL 文件。很多项目会在这些文件里说明编译依赖和特殊配置,提前读一遍能避免很多弯路。

技巧二:用script命令记录编译过程。编译出错时,完整的日志非常重要。用script命令可以把终端输出保存到文件:

script build.log ./configure && make exit

这样所有输出都会记录在build.log里,方便事后排查。

技巧三:在 screen 或 tmux 里编译。编译大项目可能耗时很长,如果 SSH 连接断开,编译进程会被终止。用 screen 或 tmux 可以保持会话:

tmux new -s build ./configure && make # 按 Ctrl+B 然后按 D 脱离会话 # 下次用 tmux attach -t build 重新连接

技巧四:用make -j并行编译。现代机器通常有多个 CPU 核心,用-j参数可以并行编译,大幅缩短时间:

make -j$(nproc)

$(nproc)会自动获取 CPU 核心数。不过要注意,并行编译有时会因为依赖顺序问题导致报错,如果遇到奇怪的编译错误,可以试试单线程编译确认是否是并行导致的问题。

技巧五:保留 configure 的缓存文件。configure 脚本会生成一个config.cache文件,记录上次检查的结果。如果你需要多次运行 configure,保留这个文件可以跳过重复检查,加快速度。不过如果环境发生了变化,建议删掉缓存重新检查:

rm config.cache ./configure

6. 一个完整的实操案例

6.1 场景描述

假设你在一台 Ubuntu 机器上编译一个名为libimageproc的图像处理库。这台机器可以访问内网,但无法直接访问外网。你执行了以下命令:

tar -xzf libimageproc-2.1.0.tar.gz cd libimageproc-2.1.0 ./configure --prefix=/usr/local

终端输出停在:

checking for gcc... gcc checking whether the C compiler works... yes checking for online model data...

然后就一直卡在这里,等了十分钟没有任何新输出。

6.2 排查过程

第一步:确认进程状态。

ps aux | grep configure

输出显示 configure 进程存在,CPU 占用率 0.0%,状态为S(睡眠)。

第二步:用 strace 定位阻塞点。

sudo strace -p <PID> -e trace=network

输出显示:

connect(3, {sa_family=AF_INET, sin_port=htons(443), sin_addr=inet_addr("203.0.113.10")}, 16) = -1 EINPROGRESS

确认进程正在尝试连接一个外部 IP 的 443 端口,但连接无法建立。

第三步:查看 configure 脚本中的相关代码。

grep -n "online model data" configure

找到对应的代码行:

$as_echo "$as_me: checking for online model data" >&6 wget -q https://example.com/models/model-data.tar.gz -O model-data.tar.gz

确认是 wget 在下载模型数据文件。

6.3 解决方案实施

方案选择:由于模型数据文件是编译所必需的,不能简单跳过。我们采用"本地缓存 + 修改脚本"的方案。

步骤一:在能上网的机器上下载模型数据文件。

wget https://example.com/models/model-data.tar.gz

步骤二:把文件传输到编译机器上。可以用 scp 或者 U 盘拷贝,放到源码目录下。

步骤三:修改 configure 脚本,跳过下载步骤。

cp configure configure.bak vim configure

找到 wget 那一行,改成:

# 原始代码 wget -q https://example.com/models/model-data.tar.gz -O model-data.tar.gz # 修改后 if [ ! -f model-data.tar.gz ]; then echo "Error: model-data.tar.gz not found" >&2 exit 1 fi

步骤四:重新运行 configure。

./configure --prefix=/usr/local

这次 configure 顺利通过,不再卡住。

步骤五:编译并安装。

make -j$(nproc) sudo make install

6.4 经验总结

这个案例的关键在于:先定位,再解决。不要一上来就盲目尝试各种方法,而是用 strace 确认阻塞位置,然后针对性地处理。另外,修改 configure 脚本之前一定要备份,万一改错了可以快速恢复。

提示:如果你经常需要在离线环境下编译各种软件,建议搭建一个内网的软件源镜像,把常用的依赖包和资源文件都缓存到内网。这样后续编译时就不需要每次都手动处理网络问题了。

7. 预防措施:让下次编译不再卡

7.1 编译前的环境检查清单

在开始编译之前,花两分钟做以下检查,可以避免大部分问题:

  • 确认build-essential已安装:dpkg -l build-essential
  • 确认pkg-config已安装:pkg-config --version
  • 确认网络环境:ping -c 1 8.8.8.8(如果无法访问外网,提前做好离线准备)
  • 确认磁盘空间充足:df -h
  • 确认源码目录权限正确:ls -la
  • 阅读项目文档中的编译说明

7.2 建立本地编译环境的最佳实践

如果你经常需要从源码编译软件,建议把编译环境标准化:

统一安装基础工具链:

sudo apt install build-essential pkg-config cmake autoconf automake libtool ccache

配置 ccache 加速重复编译:

echo 'export CC="ccache gcc"' >> ~/.bashrc echo 'export CXX="ccache g++"' >> ~/.bashrc source ~/.bashrc

建立本地依赖库缓存目录:

mkdir -p ~/build-cache

把常用的依赖库源码包和资源文件放在这个目录里,需要时直接从本地取。

使用构建脚本自动化流程:

对于经常编译的项目,可以写一个简单的构建脚本:

#!/bin/bash set -e PREFIX=/usr/local JOBS=$(nproc) echo "Configuring..." ./configure --prefix=$PREFIX echo "Building with $JOBS jobs..." make -j$JOBS echo "Installing..." sudo make install echo "Done."

把脚本保存为build.sh,每次编译时直接运行bash build.sh就行。

7.3 长期维护建议

源码编译不是一次性工作,后续升级版本时还要重复整个流程。为了减少重复劳动,可以考虑:

  • 把编译好的软件打包成 deb 包,方便在多台机器上部署
  • 用 Docker 容器封装编译环境,确保环境一致性
  • 记录每次编译的配置参数和遇到的问题,形成自己的知识库

我个人在实际操作中的体会是:编译问题的排查成本远高于预防成本。花十分钟提前检查环境,比花一个小时排查卡住的原因要划算得多。尤其是网络相关的检查,在编译之前用grep扫一遍 configure 脚本,看看有没有网络请求,能省下大量等待时间。

最后再分享一个小技巧:如果你不确定某个 configure 脚本会不会卡住,可以先用timeout 30 ./configure --help试一下。--help参数通常不会触发完整的检查流程,但如果连--help都卡住,那说明脚本本身有问题,需要直接查看脚本内容。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询