news 2026/10/13 3:18:10

Linux开发工具链:gcc、make、gdb、strace、lsof入门

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux开发工具链:gcc、make、gdb、strace、lsof入门

写 C/C++ 程序绕不开编译,程序跑挂了要调试,怀疑进程卡在哪要追踪系统调用,查端口被谁占要列文件描述符。gcc、make、gdb、strace、lsof就是 Linux 上这五件事对应的基础工具。这一篇把每条命令最常用的部分讲清楚,看完能独立编译一个小程序、用 gdb 打断点、用 strace 看进程到底卡在哪。

一、gcc:编译四阶段

语法格式

gcc [选项] 源文件 -o 输出文件

编译的四个阶段

很多人以为gcc hello.c -o hello是一步完成的,实际上它内部走了四步:预处理 → 编译 → 汇编 → 链接。每一步都能用参数单独看:

阶段命令产出干什么
预处理gcc -E hello.c -o hello.i预处理后的 C 代码展开#include、#define
编译gcc -S hello.i -o hello.s汇编代码翻译成汇编语言
汇编gcc -c hello.s -o hello.o目标文件(二进制)汇编成机器码
链接gcc hello.o -o hello可执行文件合并库、解析符号

常用参数

参数说明
-o 文件指定输出文件名
-g生成调试信息,gdb 必备
-O0-O2优化等级,0 不优化(调试用),2 常用
-Wall打开常用警告
-I 目录头文件搜索路径
-L 目录库文件搜索路径
-l 名字链接库,如-lpthread
-std=c99指定 C 标准

示例与预期输出

先写一个最简单的hello.c:

#include <stdio.h> int main() { printf("hello\n"); return 0; }

正常编译运行:

gcc hello.c -o hello ./hello

预期输出:

hello

想看预处理后的文件长什么样:

gcc -E hello.c | head -20

会看到stdio.h整个被展开进来,几千行代码堆在前面。

编译时带调试信息:

gcc -g -Wall hello.c -o hello_dbg

-g让 gdb 能对应到源码行号;不带-g也能调试,但看到的全是地址。

二、make 与 Makefile:自动编译

为什么需要 make

一个项目几十个.c文件,手敲gcc a.c b.c c.c -o app每次都要全量编译,改一个文件也得重来。make 的思路是:写一份规则文件(Makefile),告诉它每个文件依赖谁、怎么编译,它自动判断哪些文件改过、只重新编译改过的。

最小可用 Makefile

在项目目录下新建Makefile(首字母大写,无扩展名):

CC = gcc CFLAGS = -g -Wall hello: hello.o $(CC) hello.o -o hello hello.o: hello.c $(CC) $(CFLAGS) -c hello.c -o hello.o clean: rm -f hello hello.o

三条核心规则

概念说明
目标(target)hello:前面那部分,想生成的东西
依赖(prerequisite)冒号后面,生成目标需要的文件
命令(recipe)下一行必须用Tab开头,不是空格

常用命令

命令作用
make按第一个目标编译
make clean执行 clean 目标,清理产物
make -j44 个任务并行编译

示例与预期输出

make

预期输出:

gcc -g -Wall -c hello.c -o hello.o gcc hello.o -o hello

再跑一次make:

make: 'hello' is up to date.

因为hello比hello.o新,没东西要重新编。改一下hello.c再make,只会重新编译hello.o这一步,链接那步会重做但很快。

⚠️ 最常见的坑

命令行下面那一行开头必须是Tab,用空格代替会报missing separator。编辑器里把 Tab 显示出来检查一下。

三、gdb:断点调试

语法格式

gdb ./可执行文件

前提是编译时带了-g。

常用命令

gdb 命令简写作用
break mainb main在 main 函数入口打断点
break 行号b 12在当前文件第 12 行打断点
runr运行程序
nextn单步执行,不进入函数
steps单步执行,进入函数
print 变量p var打印变量值
btbt打印调用栈
continuec继续运行到下一个断点
listl看当前位置附近源码
quitq退出 gdb

示例与预期输出

gdb ./hello_dbg

进入 gdb 交互界面:

GNU gdb (Ubuntu 9.2-0ubuntu1~20.04.1) 9.2 ... (gdb)

在 main 打断点并运行:

(gdb) b main Breakpoint 1 at 0x1149: file hello.c, line 4. (gdb) r Starting program: /home/user/hello_dbg Breakpoint 1, main () at hello.c:4 4 printf("hello\n"); (gdb) n hello 5 return 0; (gdb) c Continuing. [Inferior 1 (process 12345) exited normally] (gdb) q

b main停在 main 入口,r启动,n执行一行(printf 的输出hello同时打到屏幕),c跑到底退出。

程序崩溃时用gdb ./程序 core.xxx加载 core dump 文件,bt直接看崩溃那一刻的调用栈,这是定位段错误最快的方式。

四、strace:追踪系统调用

语法格式

strace [选项] 命令

常用参数

参数说明
-e trace=open,read只追踪指定的系统调用
-p PIDattach 到正在运行的进程
-c统计每个系统调用的次数和耗时
-o 文件输出写到文件,不刷屏幕
-t每行前面加时间戳

示例与预期输出

看ls启动时做了哪些系统调用:

strace ls 2>&1 | head -15

预期输出(节选):

execve("/usr/bin/ls", ["ls"], 0x7ffd3b2c5e80 /* 58 vars */) = 0 brk(NULL) = 0x55f8b2c2a000 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory) openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3

每一行是一次系统调用:调用名、参数、返回值。ENOENT后面括号里是错误描述,不用自己查 errno 表。

只看文件相关的调用:

strace -e trace=openat,open,read,write ls 2>&1 | head -5

想看某个正在跑的进程到底在干嘛:

strace -p 12345

Ctrl+C停止追踪,原进程继续跑。

程序跑慢了想知道卡在哪:

strace -c ./slow_program

跑完会输出一张统计表,哪个系统调用占时间最多、失败次数最多,一眼看出来。

五、lsof:列出打开的文件

Linux 里一切皆文件,进程打开的文件、网络连接、管道、设备,在 lsof 里都是"打开的文件描述符"。

语法格式

lsof [选项]

常用参数

参数说明
-i :端口列出占用指定端口的进程
-i TCP列出所有 TCP 连接
-p PID列出某个进程打开的所有文件
-u 用户名列出某用户打开的文件
文件名看哪个进程打开了这个文件

示例与预期输出

查 80 端口被谁占了:

sudo lsof -i :80

预期输出:

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 1234 root 6u IPv4 12345 0t0 TCP *:http (LISTEN)

第一列进程名,第二列 PID,最后一列连接状态。知道 PID 就能kill或者去查它的配置。

查某个进程打开了哪些文件:

lsof -p 1234

会列出该进程打开的所有.so动态库、日志文件、socket。程序报"找不到配置文件"时,用这个命令看它到底从哪个路径读的,比瞎猜快。

查某个文件被谁占用:

lsof /var/log/nginx/access.log

预期输出:

COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME nginx 1235 root 7w REG 8,1 123456 100 /var/log/nginx/access.log

想删一个文件但提示"设备或资源忙",十有八九就是某个进程还开着它。用 lsof 找到那个进程,停掉再删。

六、知识扩展:strace 为什么能看到别的进程在干嘛

strace 靠的是 Linux 内核的ptrace系统调用。一个进程(strace)可以通过 ptrace 把自己"attach"到另一个进程上,之后目标进程每发起一次系统调用,内核都会把它拦下,通知 strace,再把控制权还回去。所以 strace 不需要重新编译目标程序、不需要读它的源码,只要有权限(root 或同一个用户),就能看到任何进程的系统调用轨迹。

这也解释了为什么strace -p偶尔会让目标程序变慢——每次系统调用都多了一层上下文切换。生产环境跑 strace 记得加-c做短时间采样,别挂着不管。

make 那块的依赖判断同理:它不猜你改了什么,只看每个文件的修改时间(mtime)。目标文件比依赖文件新就跳过,旧了就重新跑命令。所以手动touch一下源文件能强制 make 重新编译,删掉中间产物再make也是这个原理。理解了时间戳比较,Makefile 里那些"奇怪"的行为就都能解释了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/13 3:17:46

喜报!SGS通标青岛公司现代轻工质量标准实验室入选筑基试点

近日&#xff0c;青岛市质量标准实验室筑基试点名单&#xff08;第一批&#xff09;正式公布&#xff0c;通标标准技术服务&#xff08;青岛&#xff09;有限公司&#xff08;以下简称 “SGS通标青岛公司”&#xff09;申报的青岛市现代轻工质量标准实验室成功入选。本次试点遴…

作者头像 李华
网站建设 2026/10/13 3:17:19

从零入门大模型:小白程序员也能掌握的Agent开发秘籍(收藏版)

本文针对想学习大模型但不知从何开始的程序员&#xff0c;提出实用建议。作者指出&#xff0c;开发Agent无需深入大模型原理&#xff0c;而应侧重业务整合与流程编排。通过使用现成的大模型和开源框架&#xff0c;结合自身业务逻辑&#xff0c;普通程序员可快速上手。文章强调&…

作者头像 李华
网站建设 2026/10/13 3:17:17

多模态 RAG 明明检索到了图片,为什么最终回答还是会漏图?

从一次真实的图文问答测试出发&#xff0c;聊聊多模态 RAG 中的图文召回、证据选择、LLM 图片引用&#xff0c;以及一些工程实践中的思考。0. 一个让我重新思考多模态 RAG 的问题之前在做多模态 RAG 的过程中&#xff0c;我一直比较关注检索效果。毕竟&#xff0c;如果连正确的…

作者头像 李华
网站建设 2026/10/13 3:16:06

报价单制作怎么自动化?先分清这 4 类报价规则,别急着比工具

客户在群里问"这批货到汉堡多少钱"&#xff0c;销售翻出三张价目表、问一遍当前燃油附加费、再手工算一遍客户折扣&#xff0c;两天后才把报价单发过去。 这两天里最常被拉进去的是 IT&#xff1a;销售说系统算的不对&#xff0c;商务说价目表没同步&#xff0c;财务…

作者头像 李华