Linux 开发构建与二进制命令速查
更新时间:2026-08-28。本文是 Linux 命令速查主题第⑲族:开发构建与二进制,约 21 个命令。从源码到可执行文件要走四步——预处理、编译、汇编、链接,这一族命令就分布在这四步上。理解这条流水线,"为什么改个头文件要重编半小时"、"为什么加了
-O2断点就打不准"这类问题自然就懂了。二进制的观测与分析见 性能观测与调试,原理见 ELF 与二进制。
一、编译器
gcc(GNU Compiler Collection,GNU 编译器套件)是 Linux 上的标准编译器,g++ 是它的 C++ 前端。日常最常用的其实是两类参数:控制优化级别的(-O0~-O3)和控制调试信息的(-g)。这两者的取舍直接决定了程序好不好调、跑得快不快。
| 命令 | 作用 | 常用示例 |
|---|---|---|
gcc | C 编译器,也可驱动整个编译链接流程直接产出可执行文件 | gcc -o app main.c;gcc -c main.c(只编译不链接,产出 .o);gcc -O2 -o app main.c(优化);gcc -g -O0 -o app main.c(保留调试信息,关闭优化);gcc -Wall -Wextra(打开警告) |
g++ | C++ 编译器,会自动链接 C++ 标准库(用 gcc 编 C++ 会缺库) | g++ -std=c++17 -O2 -o app main.cpp;g++ -std=c++20 -Wall app.cpp |
gcc 常用参数 | 分阶段控制、宏定义、库与头文件路径 | -E(只预处理);-S(只编译到汇编);-c(只汇编不链接);-DDEBUG(定义宏);-I./include(头文件路径);-L./lib -lfoo(库路径与库名);-shared -fPIC(生成动态库) |
-O0 -g与-O2怎么选:调试阶段用前者,因为它保留了完整的符号和行号,断点能准确落在源码行上,变量也不会被优化掉;发布用后者。本站的 cpu-demo 实验 刻意用-O0 -g编译,正是为了让 perf 火焰图能看见符号和行号——换成-O2那条热点循环会被优化器整个消除,反而看不到想演示的东西。
二、构建系统
小项目手写 gcc 命令还行,文件一多就不可能了——你得记住谁依赖谁、哪个头文件改了要重编哪些文件。make 解决的就是依赖追踪和增量构建:只有真正变了的才重编。
| 命令 | 作用 | 常用示例 |
|---|---|---|
make | 经典构建工具,按 Makefile 里的规则做增量构建 | make;make -j8(8 并行,大幅提速);make clean(清理);make -n(只打印命令不执行);make -B(强制全量重编);make install(安装);make V=1(显示完整命令) |
cmake | 跨平台构建系统生成器,生成 Makefile 或 Ninja 文件,现代 C/C++ 项目主流 | cmake -S . -B build(配置到 build 目录);cmake --build build -j8(构建);cmake -DCMAKE_BUILD_TYPE=Release ..;cmake -DCMAKE_INSTALL_PREFIX=/usr/local .. |
ninja | 追求极致速度的构建系统,通常不手写配置而由 cmake/meson 生成 | ninja(在当前目录构建);ninja -C build;ninja -t targets(列出目标);ninja -j0(自动选并行度) |
meson | 现代化的构建系统,配置语法比 cmake 简洁,编译速度快 | meson setup build;meson compile -C build;meson test -C build;meson configure build -Dbuildtype=release |
make -j8里的数字一般设成 CPU 核心数(nproc可查),设太大会把内存吃光反而更慢。cmake 推荐用"外部构建"(cmake -S . -B build),把产物和源码分开,清理时直接删掉 build 目录,不会污染源码树。
三、库工具
静态库(.a)在链接时被完整拷进可执行文件,动态库(.so)则是在运行时才加载、可被多个程序共享。这个区别决定了:改了动态库,所有用到它的程序下次启动就自动用上新版本;改了静态库,必须重新编译链接。
| 命令 | 作用 | 常用示例 |
|---|---|---|
ar | 创建和操作静态库(.a,本质是 .o 文件的打包) | ar rcs libfoo.a a.o b.o(创建);ar t libfoo.a(列出成员);ar x libfoo.a(解出) |
ranlib | 为静态库生成符号索引,加速链接器查找(现代 ar s 已含此功能) | ranlib libfoo.a |
pkg-config | 查询已安装库的编译与链接参数,避免手工写一堆 -I/-l | pkg-config --cflags --libs openssl(输出编译链接参数);pkg-config --list-all(列出所有);pkg-config --modversion zlib |
ldconfig | 更新动态链接器的缓存与软链接,装了新 .so 后必须执行 | sudo ldconfig;ldconfig -p | grep libfoo(查缓存) |
编译时链接用
-lfoo能过、运行时却报 "cannot open shared object file",是因为运行时找不到.so。三种解法:把库路径写进/etc/ld.so.conf.d/再ldconfig;设LD_LIBRARY_PATH环境变量;或在链接时加-Wl,-rpath,/path/to/lib把路径烙进可执行文件。
四、二进制处理
编译产出的可执行文件里,除了真正会被 CPU 执行的机器码,还塞了不少"给人看、给工具看"的额外内容:符号表记录着函数名与地址的对应关系,调试信息保存了地址到源码行号的映射。正是靠这些,调试器才能把一串十六进制地址翻译成"main.c 第 42 行"。代价是文件可能比纯代码大好几倍。这一节的几个命令,基本就是围着这些额外信息做加减法。
| 命令 | 作用 | 常用示例 |
|---|---|---|
strip | 剥掉二进制里的符号表与调试信息,可显著减小体积(生产镜像常用) | strip app;strip -s(更彻底);strip --only-keep-debug app -o app.debug(分离调试信息) |
size | 显示各段(text/data/bss)的大小,做嵌入式或体积优化时用 | size app;size -A app(详细) |
ld | GNU 链接器,把 .o 与库拼成可执行文件(通常由 gcc 间接调用) | ld -o app a.o -lc -dynamic-linker /lib64/ld-linux-x86-64.so.2;ld --verbose(看默认链接脚本) |
as | GNU 汇编器,把汇编代码编译成目标文件 | as -o a.o a.s |
patch | 应用补丁文件(diff 产出)到源码,打热修复或合入上游补丁 | patch -p1 < fix.patch;patch -p1 -R < fix.patch(反向撤销);patch --dry-run -p1 < fix.patch(先演练) |
strip掉符号后程序还是能跑,只是崩溃栈只剩地址没有函数名。正确的做法是分离而不是丢弃:strip --only-keep-debug把调试信息单独存一份,需要时objcopy --add-gnu-debuglink关联回去,或者直接用addr2line -e app.debug拿带调试信息的文件去翻译地址。
五、调试与内存检查
valgrind 通过模拟 CPU 执行来检查内存问题,不需要重新编译(但会慢 10~50 倍)。它是排查内存泄漏、越界访问、使用未初始化内存的标准工具——这类 bug 往往不会立刻崩溃,而是跑几小时后才出问题,最难查。
| 命令 | 作用 | 常用示例 |
|---|---|---|
valgrind | 内存调试与性能分析工具集,最常用的是 memcheck(内存检查) | valgrind --leak-check=full ./app(详细泄漏报告);valgrind --tool=callgrind ./app(调用分析);valgrind --tool=helgrind ./app(数据竞争);valgrind --log-file=v.log ./app |
gdbserver | 在目标机上运行、由另一台机器的 gdb 远程连接,调试嵌入式或受限环境 | 目标机 gdbserver :1234 ./app;主机 gdb ./app 后 target remote host:1234 |
用 valgrind 前建议用
-g -O0编译,否则报告里的行号会不准甚至无法定位。它会让程序慢一个数量级,所以只用来跑小规模复现用例,不要指望它跑完整压测。崩溃类问题(进程已死)改看 崩溃排查。
六、自动配置与加速构建
| 命令 | 作用 | 常用示例 |
|---|---|---|
autoconf | 生成 configure 脚本,自动探测系统环境与依赖(老派 C 项目的标准流程) | autoconf(由 configure.ac 生成 configure);./configure && make && make install(经典三步) |
automake | 配合 autoconf 生成符合规范的 Makefile.in | automake --add-missing |
libtool | 屏蔽不同平台构建动态库的差异,让同一份代码能生成各平台的库 | libtool --mode=link gcc -o libfoo.la a.lo |
ccache | 编译结果缓存,重复编译时直接命中,对"改一个头文件触发全量重编"效果显著 | ccache -s(统计命中率);ccache -C(清缓存);配合 export CC="ccache gcc" 或 cmake -DCMAKE_C_COMPILER_LAUNCHER=ccache |
distcc | 分布式编译,把编译任务分发到局域网其他机器,大幅缩短大项目构建时间 | distcc gcc -c main.c;export DISTCC_HOSTS="host1 host2";distccmon-text(监控) |
ccache是投入产出比最高的优化:装好之后在 cmake 里加一行-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache,日常重编就能从几分钟降到几秒,且完全不用改代码。CI 环境配合共享缓存目录,能把流水线时间砍掉一大半。
一句话总结
构建与二进制速查:编译用 gcc/g++(调试 -O0 -g、发布 -O2),构建用 make -j/cmake/ninja,库用 ar/pkg-config/ldconfig,瘦身用 strip(记得分离而非丢弃调试信息),查内存用 valgrind,提速用 ccache——21 个命令覆盖从源码到可发布产物的完整工具链。