本文要点
strace跟踪进程与内核之间的一次次系统调用(open/read/write/connect 等),是"程序在底层到底干了什么"的照妖镜- 基本用法
strace <命令>直接跟踪命令执行;-f跟随子进程,-o输出到文件避免刷屏 -e trace=openat,write只过滤关心的调用类型;返回值-1 ENOENT能精准定位"找不到文件/配置"类故障strace -p附着到已运行的进程,排查"进程卡住/假死"时直接看它阻塞在哪个系统调用上strace -c输出系统调用统计表(次数/耗时占比),快速判断程序的调用热点
为什么需要 strace:程序与内核之间的一层"监控摄像头"
平时排查 Linux 问题时,我们能看到的都是程序"愿意展示"的:日志、退出码、报错信息。但当程序卡死、崩溃、或者"悄悄读了一个不存在的文件"时,日志往往帮不上忙。strace 提供的是另一层视角:程序每一次进入内核的请求(打开文件、读写数据、建立连接、等待信号……)都叫系统调用,strace 会把它们逐个记录下来,包括调用的参数和内核返回的结果。
换句话说,普通工具看到的是"程序说了什么",strace 看到的是"程序实际上做了什么"。很多疑难杂症(启动即崩、卡住不动、读了错误路径的配置)在系统调用层面往往一眼就能看出来。
Ubuntu 上安装很简单:
apt install strace基本用法:strace 跟踪一个命令
直接在命令前加上 strace,程序跑的过程中产生的所有系统调用都会打到标准错误输出。不过全量跟踪会很吵(启动一个命令会伴随大量加载动态库、读取 locale 等系统调用),所以实际排查时常用 -e trace= 只过滤关心的类型。先看过滤后的效果:
strace -e trace=openat,write cat /etc/hostname
输出里每行就是一个系统调用,格式是 调用名(参数) = 返回值:
openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3:cat以只读方式打开/etc/hostname,内核返回文件描述符3;write(1, "demo\n", 5) = 5:把读到的demo内容写到文件描述符1(标准输出),写了 5 个字节;- 前面还有一堆
openat(...) = -1 ENOENT (No such file or directory),是程序在探测系统里的 locale 文件路径,属于正常"尝试"行为,不是错误——判断一次调用是否真的失败,要看它是否影响了你关心的路径; - 结尾
+++ exited with 0 +++表示进程以退出码 0 正常结束。
过滤:-e trace= 只关心特定调用
全量跟踪默认会输出几十上百行。真实场景下我们往往只关心某一类系统调用,-e trace= 支持按类过滤:
strace -e trace=file cat /etc/hostname # 所有文件类调用
strace -e trace=network nc -z 1.1.1.1 443 # 网络类调用
strace -e trace=signal ./server # 信号相关
strace -e trace=read,write ./app # 只关心读写
strace -e trace=process ls # 进程创建/执行类常用类别有 file、network、process、signal、memory、desc(文件描述符相关),也可以直接写具体调用名如 -e trace=openat,或用逗号组合 -e trace=openat,write。跟踪时加上 -f 会同时跟随命令 fork/exec 出的子进程(比如 shell 脚本里的每一条命令),-o 文件名 则把输出写到文件,避免在屏幕上刷屏:
strace -f -o /tmp/trace.log bash -c 'echo hello; cat /etc/hostname'实战:程序读不到配置文件,strace 一眼定位
典型故障场景:程序报"找不到配置",但你怎么都查不出它到底在找哪个路径。用 strace 过滤文件类调用跑一次,看它实际尝试打开过哪些路径即可:
strace -e trace=openat cat /etc/absent.conf
关键两行:
openat(AT_FDCWD, "/etc/absent.conf", O_RDONLY) = -1 ENOENT (No such file or directory):程序尝试打开/etc/absent.conf,内核返回-1,错误码ENOENT(没有这个文件);cat: /etc/absent.conf: No such file or directory和+++ exited with 1 +++:cat把错误转成报错信息,进程以退出码 1 结束。
遇到类似问题,把 -e trace=openat 的输出里 = -1 ENOENT 的行找出来,就能确认程序"到底想要哪个文件、缺在哪个路径",而不是去猜。这对那种把配置路径写进编译参数、默认去多个候选目录找的程序尤其有效。
附着到已运行的进程:strace -p 排查"卡死"
程序已经跑起来并卡住了,没法重跑,怎么办?strace -p <PID> 可以附着到正在运行的进程上,实时观察它后续执行的系统调用。先 pgrep 找到 PID:
strace -p $(pgrep -f 'sleep 100') -e trace=nanosleep附着后输出格式一样,比如 nanosleep({tv_sec=100,...}) = ? 会显示进程正阻塞在 nanosleep(睡眠)调用上;如果卡在 read 或 connect 上,就能确认是等待输入还是网络连接超时。这是排查"服务假死/无响应"最直接的手段:
strace -p $(pgrep nginx) -e trace=network # 看 nginx 当前网络调用
strace -p $(pgrep -f 'java.*app') -e trace=read,write,connect # Java 进程卡顿排查-p 同时只能附着一个进程(可用 -p PID1,PID2 附着多个),结束后按 Ctrl+C 脱离,进程本身不受影响,会继续正常运行。
统计热点:strace -c 看系统调用次数与耗时
想快速了解一个程序"忙在哪里",用 -c 汇总统计而不是逐行输出:
strace -c ls /var/log
统计表按耗时占比排序,每列含义:
% time:该系统调用占用总时间的百分比;seconds:累计耗时(秒);calls:调用次数;errors:出错次数(比如 ENOENT);syscall:调用名。
对于性能问题,先看耗时占比最高的几个调用,再针对性深入;如果 calls 巨大,则可能是日志刷屏、轮询过密等。strace -c 只做概要统计,比逐行跟踪开销低得多,适合先做整体扫描。
strace 的常见坑与替代工具
几个容易踩的注意点:
- 跟踪会显著降低性能:进程每一次系统调用都要被截获记录,对高频 IO 程序可能拖慢几十倍,生产环境慎用、用完即摘;
- 会改变时序:附着本身可能改变竞态条件,偶现 bug 可能"一跟踪就消失";
- 看不到用户态内部逻辑:strace 只记录内核边界的调用,函数内部计算需要调试器(如 gdb)或
ltrace(跟踪库函数调用); - 权限限制:附着他人进程需要相同用户或 root 权限。
类似场景的工具还有 ltrace(跟踪动态库函数)、perf trace(更高效的内核跟踪,适合生产环境)、以及 eBPF 系的 bpftrace,可作为深入排查的下一步。
小结
strace 的价值在于把"程序行为"还原成一条条可见的系统调用:-e trace= 过滤、-p 附着运行进程、-c 统计热点,三个用法覆盖了故障定位最常见的三类场景——找不到文件、进程卡住、性能热点。遇到日志解释不了的问题时,先跑一次 strace,往往比继续猜更接近真相。
评论 (0)
暂无评论,快来抢沙发吧!