程序跑在服务器上,代码是别人写的,日志又没打全,你现在只知道它"卡住了""连不上数据库""打开文件报错",却不知道它到底在哪一步停住。这种时候,strace 是能让你直接看到进程和内核之间对话的工具:它把程序每一次系统调用、每一次返回值都打印出来,程序在等什么、被什么拒绝了,一目了然。
这篇文章讲清楚 strace 的原理和常用参数,重点放在怎么用它排查真实故障:程序卡在什么地方、文件为什么打不开、网络连接为什么建不起来。同时会明确告诉你一件重要的事——strace 是有性能开销的,生产环境使用要谨慎,用错了可能把线上问题放大。看完你应该能熟练地用它定位大多数"程序行为异常但日志无痕"的问题。
strace 的原理:看进程与内核的对话
应用程序想要读文件、发网络包、申请内存、创建子进程,都必须通过系统调用(system call)请求内核代为完成。普通程序没法直接操作硬件,系统调用就是那道门。strace 做的事情,是利用 ptrace 机制挂到目标进程上,在每次系统调用进入和返回时把参数和结果记录下来。
打个比方:内核是一个服务窗口,进程是来办事的人。正常情况下你只看到人进出窗口、办完事离开,但看不到他递了什么材料、窗口为什么退回。strace 相当于站在窗口旁边全程记录:谁递了什么、窗口返回了什么、等了多久。
它的输出长这样:
openat(AT_FDCWD, "/etc/nginx/nginx.conf", O_RDONLY) = 3
read(3, "#user nginx;\nworker_processes"..., 4096) = 4096
close(3) = 0
connect(4, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.0.5")}, 16) = -1 ECONNREFUSED (Connection refused)
左边是系统调用名,括号里是参数,等号后面是返回值。返回负数(如 -1)就是失败,后面跟着的错误码(如 ECONNREFUSED)说明失败原因。你要练的本事就是快速扫这些返回值和错误码,尤其是那些 -1 和耗时特别长的行。
安装很简单,多数发行版不带,需要手动装:
# RHEL / CentOS / Rocky
sudo dnf install -y strace
# Debian / Ubuntu
sudo apt update
sudo apt install -y strace
strace -V
常用参数一览
strace 参数很多,先记住下面这些就够处理九成场景:
| 参数 | 作用 | 典型场景 |
|---|---|---|
-p PID |
附加到正在运行的进程 | 追踪线上服务,不用重启 |
-f |
跟随子进程和线程 | 追踪会 fork 的程序、Java、多进程服务 |
-e trace= |
只跟踪指定类别或具体调用 | 只看文件、网络、进程相关 |
-o 文件 |
把输出写到文件 | 输出量大时避免刷屏 |
-T |
显示每次调用耗时 | 找哪一步慢 |
-t / -tt |
显示时间戳 | 看时间线 |
-c |
统计汇总,不逐条打印 | 看调用次数分布 |
-s 长度 |
字符串最大显示长度,默认 32 | 看完整的路径或报文内容 |
-y |
显示文件描述符对应的路径 | 把 3 翻译成 /etc/nginx/nginx.conf |
-e trace= 支持按类别过滤,最常用的几类:
-e trace=file # 所有文件相关(open、stat、unlink 等)
-e trace=network # 网络相关(socket、connect、sendto、recvfrom 等)
-e trace=process # 进程相关(fork、execve、clone、exit 等)
-e trace=signal # 信号相关
-e trace=desc # 文件描述符相关(read、write、close 等)
-e trace=%memory # 内存相关(mmap、brk 等)
类别前面加 ! 表示排除,比如 -e trace=!futex 可以过滤掉大量无意义的 futex 调用——这在追踪多线程程序时非常有用,因为 futex 经常刷屏。
实战一:程序卡在哪里
最常见的用法是"命令执行了但没反应",你要知道它卡在哪一步。
# 直接跟踪整个命令
strace -T -tt -s 200 curl -s http://10.0.0.5/health
最后几行通常停在某个系统调用上不动,比如卡在 connect 或 recvfrom,说明它在等网络;卡在 read,可能在等磁盘或某个管道;卡在 futex,可能是有线程锁竞争。加 -T 能看到每个调用的耗时,一眼就能看出是哪一次调用拖了很久。
对一个已经在跑的服务,用 -p 附加,不要重启它:
# 先找到 PID
ps -ef | grep nginx
pgrep -a nginx
# 附加追踪,只看网络和文件,跟随子进程
sudo strace -f -p $(pgrep -n nginx) -e trace=network,file -T -tt
纯追踪是只读的,不会改变进程行为,但它会拖慢目标进程,所以附加前要评估。默认按 Ctrl+C 就会脱离,进程继续正常运行。
下面这张对照表可以帮助快速定位卡点:
| 卡在哪个调用 | 可能原因 | 下一步查什么 |
|---|---|---|
connect |
目标端口不通、被防火墙拦、服务没起 | ss -tnp、telnet IP 端口 |
recvfrom / recv |
等对端数据、对端处理慢 | 看对端服务日志、抓包 |
read |
等磁盘 I/O、等管道/套接字输入 | iostat -x、检查上游 |
write |
磁盘满、管道缓冲区满、对端不读 | df -h、检查下游进程 |
futex |
线程锁竞争 | 属于业务逻辑问题,转移到应用层分析 |
nanosleep / poll |
程序主动等待,可能正常 | 看代码里是否有重试间隔 |
实战二:文件打不开、权限不足
"明明文件在那里,程序却说没有"这类问题,用 strace 一追就现原形,因为你能看到它实际去访问的路径。
strace -e trace=file -f -s 200 ./your_app 2>&1 | grep -E 'ENOENT|EACCES|EPERM'
常见的错误码:
| 错误码 | 含义 | 常见原因 |
|---|---|---|
ENOENT |
文件或目录不存在 | 路径拼错、工作目录不对、动态库缺失 |
EACCES |
权限不足 | 属主/权限位不对、目录没有 x 权限 |
EPERM |
操作不被允许 | 需要更高权限、被安全模块拦截 |
ENOTDIR |
路径中某段不是目录 | 中间路径写成了文件 |
EISDIR |
期望文件却是目录 | 路径指向目录 |
一个经典场景:程序报"找不到配置文件",但你觉得路径没错。用 strace 一追,可能发现它实际去找的是 /etc/myapp/config.yml 而不是 /opt/myapp/config.yml,或者它先去找相对路径、再去找其他几个位置,最后才失败。
注意 SELinux:如果权限位看起来都对,但操作仍被拒绝并返回 EACCES,很可能是 SELinux 拦的。这时要去审计日志里找 AVC 记录:
sudo ausearch -m avc -ts recent
sudo grep AVC /var/log/audit/audit.log | tail
strace 只告诉你有系统调用被拒绝,但拒绝它的是权限位还是 SELinux,要进一步确认。
实战三:网络连接问题
服务连不上数据库、请求第三方接口超时,用 strace 看网络调用最直接。
# 只看网络相关,显示时间戳和耗时
sudo strace -f -e trace=network -T -tt -s 300 -p $(pgrep -n myapp)
重点看 connect 和 sendto / recvfrom:
connect(...) = -1 ECONNREFUSED:目标端口没人监听,或服务挂了、防火墙返回了拒绝。connect(...) = -1 ETIMEDOUT:包发出去了但没人回,通常是防火墙丢包或路由不通。connect(...) = -1 EINPROGRESS:非阻塞套接字正常现象,随后要结合poll/epoll判断。sendto(...) = -1 EPIPE/ECONNRESET:连接被对端关闭。
想看程序连的是哪个地址,connect 的参数里就写明了 sin_addr 和 sin_port。配合 -y 参数,还能把文件描述符翻译成具体的 socket 或文件路径:
strace -f -y -e trace=network,file -p 1234
统计汇总:-c 与 -C
逐条输出在排查"哪里慢、哪个调用多"时信息过载,-c 汇总成一张表:
strace -c -f -p 1234
# 运行几秒后 Ctrl+C,得到统计
输出大致如下:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
62.10 0.842000 4210 200 futex
20.30 0.275000 27 1024 12 openat
8.15 0.110000 11 9990 read
5.00 0.068000 68 1000 write
% time 是各调用占用总时间的比例,calls 是次数,errors 是失败次数。如果一个进程大量时间花在 futex 上,说明线程锁竞争严重;read/write 次数极多,可能是没做缓冲、小包读写;openat 错误多,说明在反复尝试打开不存在的文件。
想要每次调用耗时统计而不是总耗时,可以配 -c -T,不过最常见的还是上面的默认统计。-C 是在正常输出之外同时给出统计,适合既想看细节又想看全局。
性能开销:生产环境慎用
这是必须强调的一点。strace 依托 ptrace,会让目标进程每进一次系统调用就停下来、被记录一次,在高频系统调用(比如高并发网络服务每秒几万次读写)的场景下,可能让目标进程慢几倍甚至一个数量级,还可能因为序列化而被拖成响应超时、触发上游熔断。
使用原则:
- 能不停机就不追大流量服务,优先在测试环境复现。
- 确实要在生产追,尽量缩短时间:用
timeout 5 strace ...包一层,或者附加后几秒就Ctrl+C。 - 用
-e trace=收窄范围,别全量打印。 - 用
-c只拿统计,输出量小,开销也相对低。 - 追多线程程序时
-f会让输出爆炸,务必配-o落盘并配合过滤。
# 限定 5 秒、只看网络、输出到文件
sudo timeout 5 strace -f -e trace=network -T -o /tmp/trace.log -p $(pgrep -n myapp)
wc -l /tmp/trace.log
如果只是想知道某进程打开了哪些文件和端口,用 lsof 比 strace 轻量得多,不必小题大做:
sudo lsof -p 1234
sudo ss -tnp
替代与补充工具
strace 不是万能的,遇到这些场景可以换个工具:
| 工具 | 用途 | 相对 strace 的优势 |
|---|---|---|
ltrace |
跟踪库函数调用 | 站在更高层,看 malloc、strcmp 等 |
lsof |
查看进程打开的文件与端口 | 轻量,不阻塞进程 |
perf |
性能采样分析 | 开销小,适合生产做火焰图 |
bpftrace |
eBPF 动态追踪 | 内核态可编程,开销极低 |
tcpdump / wireshark |
网络抓包 | 看完整报文,网络问题更细 |
用 eBPF 做系统调用统计,几乎不影响性能,是生产环境的更优解:
# 需安装 bcc-tools
sudo execsnoop-bpfcc
sudo syscount-bpfcc -p 1234 -L
常见问题
追踪程序时输出太多刷屏,怎么办?
用 -o 文件 落盘,再用 grep 过滤关键字,比如只看失败调用:grep '= -1' /tmp/trace.log。再加 -e trace= 收窄类别,以及用 -e trace=!futex,clock_gettime 排除噪声调用。
为什么附加到进程后看不到任何输出?
两种情况:一是该进程系统调用确实很少,可能在做纯计算;二是它内部开子进程/线程干活,而你没加 -f,只看到了主进程。加 -f 再试。还有一种是被 ptrace_scope 限制,需要 root 权限或调整 /proc/sys/kernel/yama/ptrace_scope。
strace 会不会把进程弄挂?
正常追随时 Ctrl+C 只是脱离,进程继续跑。但如果追踪期间进程被强杀,或者目标本身依赖 ptrace(某些调试器、容器运行时),可能互相干扰。所以生产附加前要有回滚预案,最好先测试。
内容被截断,看不到完整路径或报文?
默认字符串只显示 32 字节。加 -s 300 或更大。想看文件描述符对应的实际路径,加 -y。
权限不足报 ptrace: Operation not permitted?
非 root 用户默认只能追自己的子进程。用 sudo 提权,或检查 cat /proc/sys/kernel/yama/ptrace_scope:值为 0 允许附加任意同用户进程,值为 1 只允许追子进程。修改是临时的,需要持久化要写系统配置。
总结
strace通过ptrace记录进程的每次系统调用,是把"程序行为异常"变成"看得见的调用序列"的核心工具。- 记牢几个保命参数:
-f跟子进程、-p附加运行中进程、-e trace=file/network/process过滤、-T看耗时、-c看统计、-s放宽字符串长度、-o落盘。 - 卡点看最后停住的调用,失败看返回
-1的错误码(ENOENT、EACCES、ECONNREFUSED各代表不同问题),慢点看-T和-c的耗时。 - 生产环境慎用,
ptrace会给高频系统调用的服务带来显著额外开销;能用lsof、ss、bpftrace/perf解决的,就别上strace。 - SELinux 拦截会表现为
EACCES,要配合ausearch -m avc确认,别只在权限位里找原因。
企业QQ咨询




