strace —— 看进程在跟内核要什么
strace —— 看进程在跟内核要什么
分类:02 进程与网络诊断 适用:Linux(ptrace 系统调用) 一句话:进程卡住、启动失败、文件找不到、连不上,
strace能给出最直接的证据。
1. 它解决什么问题
- 服务启动报错但日志没写清楚,到底卡在哪一步?
- 进程 CPU/IO 异常,是在疯狂
read还是futex等锁? - "配置文件明明改了却不生效"——它到底读了哪个路径?
- 连接超时——是 DNS、connect 还是 TLS 握手?
2. 基本用法
strace <command> # 跟踪一个新命令
strace -p <PID> # 跟踪已运行进程
strace -f -p <PID> # 跟踪该进程及其所有子进程/线程
strace -o /tmp/trace.log <cmd> # 输出到文件(推荐,避免刷屏)
strace -c <cmd> # 只做统计汇总:各系统调用次数与耗时
strace -c -p <PID> # 对运行中进程统计(需 Ctrl-C 结束才输出)
3. 关键参数
| 参数 | 作用 | 使用建议 |
|---|---|---|
-f |
跟踪子进程/线程 | 服务类进程几乎必加 |
-T |
显示每个调用耗时(秒) | 找慢调用 |
-tt |
带微秒时间戳 | 与日志对齐时间 |
-r |
相对时间差 | 看调用间隔 |
-s <n> |
字符串最大长度(默认 32) | 看完整路径/请求体用 -s 1000 |
-y |
打印文件描述符对应的路径 | 强烈推荐,-y 让 fd=7 变成 fd=7</etc/nginx/nginx.conf> |
-yy |
还打印 socket 的 IP:端口 | 网络排障神器 |
-e trace=<set> |
只跟踪指定调用 | 降噪关键 |
-e signal=<sig> |
只显示指定信号 | |
-e status=successful/failed |
只显示成功/失败调用 | 找报错 |
-k |
打印用户态调用栈 | 定位"谁调用的" |
-w |
显示调用间的时间差 | 分析阻塞 |
-D |
作为独立进程运行 | 减少对被跟踪进程的干扰 |
-Z |
只显示失败调用 | 快速找错误 |
常用 trace 集合
-e trace=file # 所有文件相关(open/stat/access/unlink...)
-e trace=network # socket/connect/sendto/recvfrom...
-e trace=process # fork/clone/execve/exit
-e trace=signal # 信号
-e trace=ipc # 进程间通信
-e trace=desc # 文件描述符操作(read/write/close...)
-e trace=openat,read,write
4. 输出怎么读
openat(AT_FDCWD, "/etc/nginx/nginx.conf", O_RDONLY|O_CLOEXEC) = 3
read(3, "# user nginx;\nworker_processes", 4096) = 4096
connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.0.5")}, 16) = -1 ETIMEDOUT (Connection timed out)
结构:系统调用(参数...) = 返回值 [errno] <耗时>。
常见返回值:
| 返回 | 含义 |
|---|---|
= 3 |
成功,返回 fd 或字节数 |
= -1 ENOENT |
文件/路径不存在 |
= -1 EACCES |
权限不足(常见于 SELinux 也会报这个) |
= -1 EAGAIN |
非阻塞暂无数据 |
= -1 ETIMEDOUT |
连接超时 |
= -1 ECONNREFUSED |
端口没监听或被防火墙 reject |
= ? |
进程退出/被信号中断,未返回 |
5. 实战场景
5.1 服务启动失败,看它到底缺什么
strace -f -e trace=file -s 200 -o /tmp/trace.log /usr/local/bin/myapp
grep -E 'ENOENT|EACCES' /tmp/trace.log | tail -30
ENOENT 不一定是错误(程序会尝试多个候选路径),重点看最后失败的那次。
5.2 定位进程卡在哪
strace -f -p <PID> -T -tt -s 200
- 停在
futex(...)很久 → 等锁,去看线程栈(jstack、gdb、py-spy)。 - 停在
read(...)很久 → 等数据,可能是下游不响应。 - 停在
connect(...)→ 网络不通,配合-yy看目标地址。
5.3 找出真正读取的配置文件
strace -f -e trace=openat -y -s 300 -p <PID> 2>&1 | grep -i conf
解决"我改了 A 文件,程序读的是 B 文件"这类经典问题。
5.4 统计性能热点(系统调用层)
strace -c -f -p <PID>
# 跑 30 秒后 Ctrl-C
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
62.31 1.245678 120 10380 futex
15.20 0.303912 45 6750 read
8.10 0.162000 12 13500 write
futex 占大头 → 锁竞争;read/write 占大头且 calls 巨大 → 小 IO 过多,考虑缓冲。
5.5 网络连接问题
strace -f -yy -e trace=network -s 500 -p <PID>
# connect(5, {sa_family=AF_INET, sin_port=htons(6379), sin_addr=inet_addr("10.0.0.9")}, 16) = -1 ETIMEDOUT
直接给出目标 IP 和端口,比翻日志快。
5.6 抓 execve,看进程被谁拉起
strace -f -e trace=execve -s 500 -p <PID>
6. 常见坑
- 性能开销大:
strace会让被跟踪进程慢几倍到几十倍,生产环境只做短时抓取,用完立刻Ctrl-C。 ptrace_scope限制:Ubuntu 默认kernel.yama.ptrace_scope=1,只能跟踪自己的子进程。跟踪其他进程需 root 或临时sysctl -w kernel.yama.ptrace_scope=0。- 容器内权限不足:需要
SYS_PTRACEcapability,docker run --cap-add=SYS_PTRACE或在宿主机上抓。 - 必须
-f:不跟踪子进程,服务 fork 后你就只看到父进程在wait4,误以为"啥也没干"。 - 不加
-y看不懂 fd:read(7, ...)里 7 是什么全靠猜。 -c需要正常退出才输出统计:直接 kill 会丢结果,用Ctrl-C。- 输出量巨大:务必
-o file,且用-e trace=收窄。 = ?不是错误:进程被信号中断时调用没返回。EACCES可能是 SELinux:strace看到权限拒绝时,同时查ausearch -m avc -ts recent。- Alpine/minimal 镜像没装 strace:
apk add strace,且 busybox 环境部分调用名不同。
7. 速查表
strace -f -p <PID> # 跟所有线程
strace -f -T -tt -y -s 200 -o /tmp/t.log <cmd> # 完整记录
strace -c -f -p <PID> # 统计热点
strace -f -e trace=file -p <PID> # 文件相关
strace -f -e trace=network -yy -p <PID> # 网络相关
strace -f -e trace=openat -p <PID> 2>&1 | grep conf
strace -e status=failed -p <PID> # 只看失败
strace -f -e trace=execve -p <PID> # 谁被启动
# 相关
cat /proc/<PID>/stack # 内核栈(D 状态)
cat /proc/<PID>/syscall # 当前系统调用
perf trace -p <PID> # 低开销替代