AI Infra 训练营
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub
总览
  • Day 1 · 集群起步 + CNI
  • Day 2 · 控制面 + etcd
  • Day 3 · CRD + Operator + Webhook
  • Day 4 · 存储深度
  • Day 5 · 卷扩容 + 安全
  • Day 6 · 调度 + 可观测
  • Day 7 · Harbor + ArgoCD + Mesh
  • Day 8 · AI Infra
  • Day 9 · Triton + GPU
  • Day 10 · MIG + HPA + 量化
  • Day 11 · AI Agent 端到端
  • Day 12 · 灾备
  • Day 13 · Operator + 联邦 + Mesh + RAG
  • Day 14 · CKA / CKS + 总结
  • LLM 训练手册
  • RAG + Agent 手册
  • 推理优化手册
  • 上下文工程手册
  • Agent 开发手册
  • 面试深度复盘
  • 训练 v2 深度手册
  • 心智模型
  • 看懂命令输出
  • 容器网络底层
  • K8s 网络深入
  • DNS 全套
  • 故障排查方法论
  • 心智模型
  • 容器挂载完整指南
  • K8s Volumes 大全
  • PV/PVC/CSI 深入
  • NFS 深入
  • 分布式存储概览
  • 故障排查 runbook
HiHuo 主站
GitHub

strace —— 系统调用追踪

一句话定义

strace 拦截并显示一个进程的所有系统调用(open / read / write / connect / mmap 等)—— 把"用户态 ↔ 内核态"的所有交互打出来。是"应用看着卡死但不知道为啥"的最后手段调试工具。

典型场景

  • 应用卡死:strace -p <PID> 看它停在哪个 syscall
  • 启动失败但没日志:strace ./myapp 看它打开了什么文件 / connect 哪里失败
  • DNS 解析慢:strace -e network ./myapp
  • 文件找不到:strace -e openat ./myapp 看它在哪些路径找
  • 性能 profiling:strace -c ./myapp 统计每个 syscall 的累计耗时

⚠️ strace 让被追踪进程慢 10-100 倍。生产慎用,不要长时间挂在关键服务上。


装

apt install -y strace            # Debian / Ubuntu
yum install -y strace            # CentOS / RHEL

容器里通常没装(要 CAP_SYS_PTRACE)。


基础用法

strace ./myapp                            # 直接启动并追踪
strace -p <PID>                            # attach 到运行中的进程
strace -p <PID> -p <PID2>                  # 多个进程

输出(每行一个 syscall):

openat(AT_FDCWD, "/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=12345, ...}) = 0
mmap(NULL, 12345, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f1234567000
close(3)                                = 0
openat(AT_FDCWD, "/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
...
read(0, "hello\n", 4096)                = 6
write(1, "hello\n", 6)                  = 6
exit_group(0)                           = ?

每行格式:

syscall(args...) = return_value

负数返回值通常是错误(带 errno 名字):

openat(AT_FDCWD, "/nonexistent", O_RDONLY) = -1 ENOENT (No such file or directory)

核心 8 个 flag

strace -p <PID>                  # attach 现有进程
strace -f cmd                    # follow fork(追踪子进程)
strace -e <expr> cmd             # 过滤
strace -o file.txt cmd           # 输出到文件
strace -c cmd                    # 统计 summary(不显示每条)
strace -tt cmd                   # 加时间戳(微秒)
strace -T cmd                    # 显示每个 syscall 的耗时
strace -s 200 cmd                # 显示字符串前 200 字符(默认 32)

过滤 -e —— 让输出可读

不加过滤 strace 一秒输出几千行,根本看不过来。永远加 -e。

按 syscall 名

strace -e openat                          # 只看 openat
strace -e openat,read,write               # 多个
strace -e 'openat,close'                  # 等价
strace -e '!read,!write'                  # 排除 read / write

按 syscall 类别

strace -e trace=file                      # 文件操作(open/stat/chmod/...)
strace -e trace=network                   # 网络(socket/connect/bind/...)
strace -e trace=process                   # 进程管理(fork/exec/wait/exit/...)
strace -e trace=signal                    # 信号
strace -e trace=ipc                       # IPC(pipe/socketpair/shmget/...)
strace -e trace=memory                    # 内存(mmap/brk/munmap/...)
strace -e trace=all                       # 全部(默认)

K8s 排错最常用:

strace -e trace=network -p <PID>          # 看网络
strace -e trace=file -p <PID>             # 看文件

跟踪子进程 -f

很多应用 fork 出子进程 / 多线程。默认 strace 只看主进程:

strace nginx                              # 只追踪 master,看不到 worker
strace -f nginx                           # 含所有子进程 / 线程(**必加**)

输出会带 PID 前缀:

[pid 1234] read(0, ...) = 5
[pid 1235] write(1, ...) = 5

attach 现有进程 -p

strace -p 1234                            # attach 到 PID 1234
strace -fp 1234                           # 含线程
strace -tt -e network -p 1234             # attach + 过滤

Ctrl-C 退出 detach(进程继续跑)。

注意:strace attach 时会让目标进程慢 10-100 倍。生产服务慎用,特别是延迟敏感的(如 etcd / apiserver)。


实用场景

场景 1:应用卡死,看停在哪

$ ./myapp
# 卡住,没输出

# 另一个 shell
$ pgrep myapp
12345

$ strace -p 12345
# 停在某个 syscall
# read(3, ...)                            ← 正在等读 fd 3
# 或者
# futex(0x..., FUTEX_WAIT_PRIVATE, ...)   ← 等锁
# 或者
# connect(4, {sa_family=AF_INET, sin_port=htons(443), ...}, 16)   ← TCP 建连

futex 在等:通常是 mutex 死锁、condition variable 等通知。 read 在等:等 stdin / socket / pipe。 connect 在等:网络问题。

场景 2:启动失败、没日志

$ ./myapp
Segmentation fault   ← 啥都没说

$ strace ./myapp 2>&1 | tail -30
# 看最后几个 syscall,通常能看出
# openat(AT_FDCWD, "/etc/myapp/config.yaml", O_RDONLY) = -1 ENOENT
# write(2, "config not found", 16)
# exit_group(1)

最后几行通常给你错误根因。

场景 3:找文件在哪些路径找

strace -f -e openat ./myapp 2>&1 | grep ENOENT
# openat(AT_FDCWD, "/etc/ld.so.preload", O_RDONLY|O_CLOEXEC) = -1 ENOENT
# openat(AT_FDCWD, "/etc/myapp/config.yaml", O_RDONLY) = -1 ENOENT
# openat(AT_FDCWD, "./config.yaml", O_RDONLY) = -1 ENOENT
# openat(AT_FDCWD, "/usr/local/etc/myapp/config.yaml", O_RDONLY) = -1 ENOENT

明白应用在哪些路径找 config。把 config 放对地方就好。

场景 4:DNS 解析慢

strace -tt -e trace=network ./myapp 2>&1 | head -30
# 14:30:00.123 socket(AF_INET, SOCK_DGRAM, ...) = 3
# 14:30:00.124 connect(3, {sa_family=AF_INET, sin_port=htons(53), ...}, 16) = 0
# 14:30:00.125 sendto(3, "...", 64, 0, ...) = 64
# 14:30:05.130 recvfrom(3, ...)   ← 5 秒后才回,DNS 慢

时间戳显示 DNS 查询用了 5 秒。

场景 5:性能 profile:-c

strace -c ./myapp

跑完输出统计:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.23    0.123456         100      1234       50 read
 30.12    0.082345          80      1029           write
 15.06    0.041234         200       206           connect
  ...

知道哪类 syscall 最耗时。

或者 attach:

strace -c -p <PID>            # Ctrl-C 之后输出 summary

训练营场景

1. K8s 容器启动失败

# pod 报 CrashLoopBackOff
kubectl logs my-pod --previous       # 没明显日志

# 进节点
ssh m4
PID=$(crictl inspect <last-container-id> | jq '.info.pid')

# 重新启动容器,并在它启动瞬间 strace
# (困难因为容器进程已经退出,需要在容器再次启动时及时抓)

# 或者:用 kubectl debug 起一个相同镜像 + strace
kubectl debug my-pod -it --image=ubuntu --share-processes -- bash
# 在里面 apt install strace 后 strace -p <PID>

2. kubelet 卡住

strace -p $(pgrep kubelet) -ttt -T -e trace=network 2>&1 | head -50
# 看 kubelet 在跟谁 connect、是不是在等 apiserver 回应

3. 应用读不到 ConfigMap

# 进 pod
PID=$(crictl inspect ... | jq '.info.pid')
nsenter -t $PID --mount strace -e openat -p $PID 2>&1 | grep config
# 看它在哪些路径找 config 文件

4. 内存暴涨

strace -e trace=memory -p <PID> 2>&1 | head -100
# 看 mmap / brk 是不是疯狂调用

看字符串完整 -s 200

默认 strace 把字符串截断到 32 字符:

write(1, "Hello, this is a very long messa"..., 50) = 50

加 -s 200(或 -s 0 不限制)看完整:

strace -s 200 -e write -p 1234

排查"应用发出什么内容"必加。


strace vs ltrace vs perf

工具看什么性能开销
strace系统调用大(每次 syscall 暂停)
ltrace库函数调用(libc 等)大
perf trace类似 strace 但基于 eBPF小
bpftrace任意 kernel hook小

新内核(5.0+)有 eBPF 的 perf trace / bpftrace,性能开销小、生产更安全。但 strace 最普及、最易用。


常见踩坑

坑 1:strace 让进程慢 100 倍

strace -p $(pgrep nginx)
# nginx 处理能力骤降

strace 每个 syscall 都暂停进程、复制数据、写输出。生产高 QPS 服务不要长时间挂着。

应急选 eBPF 工具或者抓样本(短时间 detach):

strace -p <PID> -e trace=network &
STRACE_PID=$!
sleep 5
kill $STRACE_PID                    # 5 秒后 detach

坑 2:容器里跑 strace 报权限

strace -p 1234
# strace: ptrace(PTRACE_ATTACH, ...): Operation not permitted

K8s pod 默认没 CAP_SYS_PTRACE。两个办法:

  1. pod spec 加 capability:
    securityContext:
      capabilities:
        add: ["SYS_PTRACE"]
    
  2. 在节点上跑 strace(节点有 root):
    ssh m4
    strace -p <container-pid>
    

坑 3:看不到子进程

strace nginx
# 只看到 master 的几个 syscall,然后没动了

nginx 主进程 fork 出 worker 之后自己不工作了。必加 -f:

strace -f nginx

坑 4:strace 输出太多无法消化

strace -p 1234
# 一秒几千行,看不过来

永远过滤:

strace -e openat -p 1234              # 只看 openat
strace -e trace=network -p 1234       # 只看网络

或者写文件后慢慢看:

strace -p 1234 -o /tmp/strace.log &
sleep 5
kill %1
less /tmp/strace.log

坑 5:误判 syscall 错误

openat(AT_FDCWD, "/etc/x.conf", O_RDONLY) = -1 ENOENT

看到 ENOENT 第一反应"配置缺失"——但很多应用就是这样"探测"路径:先试 /etc/x.conf、不在的话再试 /usr/local/etc/x.conf、再试 ./x.conf。

只有当应用用完所有路径仍然没找到才是真的问题。看上下文。

坑 6:-c 在 attach 模式下要等结束

strace -c ./myapp
# myapp 结束后才打统计

strace -c -p 1234
# 必须 Ctrl-C 退出(attach 模式)才打统计

不到结束看不到统计。

坑 7:strace 在静态链接二进制上输出不一样

静态链接的二进制(Go 程序常见)不通过 libc loader,看到的 syscall 路径完全不同——没有那一堆加载 libc / libpthread 的 openat。

不算坑,但Go 程序的 strace 一开始看着特别干净,习惯了 C 程序看 strace 的人容易困惑。

坑 8:strace 影响 close / read 的语义

少数情况下,strace 拦截 syscall 会让 race condition / timing 发生改变,有 bug 在 strace 下不出现、不在时出现——经典海森堡 bug。

修法:换 eBPF 工具(更低侵入),或者用日志。


strace 的"3 类排错口诀"

现象strace 滤什么
卡住不动-p <PID> 直接看停在哪
启动失败strace -e openat,connect ... 看最后几行
网络慢strace -tt -T -e trace=network 看耗时
文件找不到strace -e openat 2>&1 | grep ENOENT
内存暴涨strace -e trace=memory
突然 segfaultstrace -e signal 或看末尾几行

关联命令

  • ltrace —— 追踪库函数调用(libc)
  • perf trace —— 性能更好的 strace(eBPF)
  • bpftrace —— 通用 eBPF 追踪
  • lsof —— 看进程开了哪些文件(静态视角)
  • ps / /proc/<PID>/wchan —— 看进程在等什么内核函数(不需要 attach)
  • gdb —— 真正 debug 进程内部状态
在 GitHub 上编辑此页