第 6 章 · 08 系统调用追踪: 、 本节摘要:这是本章的「终极武器」。当你把 、 、 都试过了,还是搞不清「程序为什么卡住」「它到底在读哪个文件」「为什么连不上」,就该上 了。它能打印进程的每一个系统调用(system call)——open/read/write/connect/futex...,等于给程序装了「行车记录仪」。本节要讲清 的三个核心用法(直接跑、attach 运行中进程、过滤特定调用),以及它的代价(性能开销大,只在必要时用)。 是排查疑难杂症的最后手段,也是深入理解「程序与内核如何交互」的窗口。 内容来源:原项目「Linux 命令大全」 ,精选并套用体系化模板。
strace、ltrace本节摘要:这是本章的「终极武器」。当你把
ps、top、lsof都试过了,还是搞不清「程序为什么卡住」「它到底在读哪个文件」「为什么连不上」,就该上strace了。它能打印进程的每一个系统调用(system call)——open/read/write/connect/futex...,等于给程序装了「行车记录仪」。本节要讲清strace的三个核心用法(直接跑、attach 运行中进程、过滤特定调用),以及它的代价(性能开销大,只在必要时用)。strace是排查疑难杂症的最后手段,也是深入理解「程序与内核如何交互」的窗口。
内容来源:原项目「Linux 命令大全」
command/strace.md,精选并套用体系化模板。
阅读完本节,你应当能够:
strace 的工作原理:拦截并打印进程的每个系统调用及其参数、返回值。strace 命令 直接跑并追踪、用 strace -p PID attach 运行中的进程。-e trace=open,read 过滤只看特定调用,过滤掉噪音。-f 跟踪子进程(fork 出来的)。strace 输出里的「卡住信号」(某调用长时间不返回)。strace 的性能代价,懂得它是「最后手段」而非日常工具。程序跑起来时,大部分时间在用户态执行自己的代码,但当它需要「内核才能做的事」时——读文件、收发网络、创建进程、分配内存——就发起一次系统调用(system call),陷入内核态,内核做完再把结果返回给程序。
strace 利用内核的 ptrace 机制,拦截进程的每次系统调用,打印调用名、参数、返回值:
open("/etc/config", O_RDONLY) = 3 # 打开文件, 得到 fd 3 read(3, "key=value\n", 4096) = 10 # 从 fd 3 读到 10 字节 close(3) = 0 # 关闭 fd 3 connect(4, {sa_family=AF_INET, port=80, ...}, 16) = -1 ECONNREFUSED # 连接被拒
从这段输出你能看出:程序打开了 /etc/config、读了内容、然后试图连接 80 端口但被拒绝了(ECONNREFUSED)——「为什么连不上」的答案就在这里。这种「程序与内核交互的全程记录」,是 strace 独有的能力,别的工具给不了。
strace ./myapp # 追踪 myapp 的所有系统调用(输出很多) strace -o trace.log ./myapp # 输出到文件, 不污染终端
strace -p 1234 # attach 到正在跑的进程 1234 # 按 Ctrl+C 停止追踪(不会杀掉被追踪的进程)
这是排查「已经在跑的服务突然卡住」的标准操作——不重启它,直接 attach 看。
原始 strace 输出量巨大(每个 read/write 都打印),必须过滤:
strace -e trace=open,openat ./myapp # 只看文件打开 strace -e trace=network ./myapp # 只看网络相关 strace -e trace=connect ./myapp # 只看连接(排查连不上) strace -e trace=process ./myapp # 只看进程相关(fork/exec) strace -e trace=file ./myapp # 只看文件操作
strace -f ./myapp # -f: 也跟踪 fork 出来的子进程
不加 -f,子进程的系统调用不会被追踪——这是排查「主进程没事但子进程卡住」时的常见坑。
strace -T ./myapp # 显示每个调用的耗时(秒) strace -tt ./myapp # 显示每个调用的时间戳(微秒级) strace -r ./myapp # 显示相邻调用的相对时间(找"停顿"在哪)
某条调用后面的时间特别长(比如 read(3, ...) = 10 <5.234>),就是它卡了 5 秒——直接定位「卡在哪个系统调用」。
strace -p <PID> -T # attach, 看时间 # 如果输出停在某一行不动, 那一行就是它在等的调用 # 常见: read(...) 不返回(等网络/输入)、futex(... 等锁
strace -e trace=open,openat ./myapp 2>&1 | grep -i ENOENT # 看哪些 open 返回 -1 ENOENT(文件不存在), 精确定位"它在找哪个文件"
strace -e trace=connect ./myapp # 看 connect 的返回值: ECONNREFUSED(被拒)、ETIMEDOUT(超时)、EHOSTUNREACH(不可达)
strace 让每个系统调用都多一次 ptrace 拦截,程序会慢 5-50 倍。绝对不要在生产环境对高负载服务长时间 attach——会让服务雪崩。排查完立即 Ctrl+C 脱离。
strace ./myapp | head -100 # 前几屏都是 read/write 的字节流, 找不到关键信息 strace -e trace=open ./myapp # 一定要过滤!
-f 漏掉子进程strace ./server # 主进程没事, 但主进程 fork 的 worker 卡了, 你看不到! strace -f ./server # 加 -f 才能看到子进程
strace 追踪系统调用(程序→内核);ltrace 追踪库函数调用(程序→动态库,如 malloc/printf)。两者层次不同,排查「内存泄漏(看 malloc/free)」用 ltrace 更合适,排查「卡在 IO/网络」用 strace。
strace 打印进程的每个系统调用及其参数、返回值,是程序的「行车记录仪」。strace 命令(直接跑)、strace -p PID(attach 运行中进程)、-e trace=xxx(过滤特定调用)。-f 跟踪子进程(不加会漏掉 fork 出来的进程);-T/-tt 显示时间(找「卡在哪」)。strace(系统调用)vs ltrace(库函数):排查 IO/网络用 strace,排查内存泄漏用 ltrace。本章「进程、作业与性能观测」全部讲完。从 ps/top 看进程,到信号与 kill 控制进程,到 free/vmstat 观测系统,再到 lsof/strace 深度排查——你已拥有了一套从「看见」到「读懂」到「控制」到「终极诊断」的完整进程排查工具链。下一章我们把视角从「本机进程」延伸到「网络连通」,继续扩展这套工具链。