strace —— 看进程在跟内核要什么

运维工具手册 02-进程与网络诊断 4.5 KB 原文

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. 常见坑

  1. 性能开销大:strace 会让被跟踪进程慢几倍到几十倍,生产环境只做短时抓取,用完立刻 Ctrl-C。
  2. ptrace_scope 限制:Ubuntu 默认 kernel.yama.ptrace_scope=1,只能跟踪自己的子进程。跟踪其他进程需 root 或临时 sysctl -w kernel.yama.ptrace_scope=0。
  3. 容器内权限不足:需要 SYS_PTRACE capability,docker run --cap-add=SYS_PTRACE 或在宿主机上抓。
  4. 必须 -f:不跟踪子进程,服务 fork 后你就只看到父进程在 wait4,误以为"啥也没干"。
  5. 不加 -y 看不懂 fd:read(7, ...) 里 7 是什么全靠猜。
  6. -c 需要正常退出才输出统计:直接 kill 会丢结果,用 Ctrl-C。
  7. 输出量巨大:务必 -o file,且用 -e trace= 收窄。
  8. = ? 不是错误:进程被信号中断时调用没返回。
  9. EACCES 可能是 SELinux:strace 看到权限拒绝时,同时查 ausearch -m avc -ts recent。
  10. 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>          # 低开销替代