当前位置:首页>Linux>每天学一个Linux命令系列(35):strace - 进程卡住?看系统调用秒懂

每天学一个Linux命令系列(35):strace - 进程卡住?看系统调用秒懂

  • 2026-10-11 05:40:15
每天学一个Linux命令系列(35):strace - 进程卡住?看系统调用秒懂
运
运维少年 · 科技观察

这个命令是干啥的

strace 是 Linux 上的系统调用追踪器。它能拦截并记录进程发起的每一个系统调用(syscall),包括调用的参数、返回值、耗时。简单说,strace 能让你看清:你的程序到底在干什么、卡在哪里、为什么慢。

我第一次用 strace 是在定位一个 Tomcat 启动慢的问题。启动日志卡在某个地方不动,没有报错、没有异常、什么都没有。用 strace -p PID 一看,发现进程一直在 open() 一个根本不存在的配置文件,超时后才继续。找到问题后修复,启动时间从 3 分钟降到 10 秒。从那天起,strace 就成了我的"程序卡住三件套"之一(另外两个是 jstack 和 tcpdump)。

strace 能帮你回答这些问题:
- 我的程序在卡在哪个 syscall 上了?
- 哪个文件加载失败了?
- 为什么程序这么慢?哪一步耗时最多?
- 这个配置到底被读了没有?

基本用法(3分钟上手)

追踪正在运行的进程

# 追踪 PID 为 12345 的进程
strace -p 12345

输出会哗哗地往下滚,每一行都是一个系统调用。你会看到类似 read()、write()、open()、close() 这些。

# 只看一段时间,然后 Ctrl+C 退出
strace -p 12345

默认 strace 会把所有 syscall 打印到终端,信息量巨大。别慌,后面有过滤方法。

追踪新启动的命令

# 启动命令并同时追踪它的系统调用
strace ls -l

# 追踪 curl 发请求过程
strace curl https://www.baidu.com

直接在命令前面加 strace 就行,它会 fork 一个子进程来执行。

统计系统调用

# 统计每个 syscall 的调用次数和耗时,-c 是统计模式
strace -c -p 12345

等个几秒钟 Ctrl+C,strace 会打印一张统计表:

% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 45.23    0.123456       12.34     10000           read
 30.12    0.089012        8.90     10000           write
 10.05    0.030000       30.00      1000           open
...

一眼就能看出哪个 syscall 最耗时间。我经常用 -c 做性能初筛。

进阶骚操作

按系统调用类型过滤

# 只看文件打开和读文件的操作
strace -e trace=open,openat,read -p 12345

# 只看网络相关的 syscall
strace -e trace=network -p 12345

# 只看进程管理的 syscall(fork, exec, clone)
strace -e trace=process -p 12345

# 只看信号相关的 syscall
strace -e trace=signal -p 12345

过滤是 strace 的精髓,不然输出量太大根本看不完。

显示时间戳

# 显示每个 syscall 的绝对时间(精确到秒)
strace -t -p 12345

# 显示相对时间(精确到微秒,从第一个 syscall 开始计时)
strace -r -p 12345

# 显示每个 syscall 的耗时(syscall 执行了多久)
strace -T -p 12345

我最爱 -t 结合 -T,一个看什么时候调用的,一个看花了多久:

# 又看时间又看耗时
strace -t -T -p 12345

输出重定向到文件

# 把 strace 输出保存到文件,方便慢慢看
strace -o /tmp/strace.log -p 12345

# 用 -ff 分离子进程的输出,每个进程一个文件
strace -ff -o /tmp/strace_child -p 12345

不加 -o 的话,输出直接刷屏,手滑 Ctrl+C 重来又得抓一次。

限制追踪的 syscall 数量和字符串长度

# 只追踪前 100 个 syscall
strace -c 100 -p 12345

# 限制打印的字符串长度,默认 32 字节
strace -s 256 -p 12345

-s 控制打印参数里字符串的长度。如果你在追踪 HTTP 请求的内容,默认 32 字节太短,加 -s 8192 基本够看。

避坑指南

1. strace 会让进程变慢

这是最重要的一点。strace 在每一个 syscall 前后都要做 interception,性能开销很大。根据经验,加了 strace 的程序会慢 10-100 倍。

# 追踪一个频繁 IO 的程序,速度会肉眼可见地变慢
strace -p 12345

我的建议:
- 不要在压测的时候跑 strace
- 用 -c 统计模式比逐条打印轻量
- 用 -e trace= 精确过滤,减少 overhead
- 只追踪一小段时间,抓到关键信息就停

2. strace 看不到 sleep 和 idle

如果你看到输出停在了 nanosleep() 或者 select() 上,那不是程序卡了,是它在正常等待:

# 程序在 sleep,不是在卡
nanosleep({tv_sec=5, tv_nsec=0}, ...

需要判断是"正常等待"(poll, select, epoll_wait)还是"异常卡住"(反复失败的 connect、慢速的 open、无响应的 read)。

3. 权限不足

# 非 root 用户 strace 其他用户的进程会报错
strace -p 1
# 报错: strace: attach: ptrace(PTRACE_SEIZE, 1): Operation not permitted

# 需要用 root 或者 sudo
sudo strace -p 1

大部分生产环境需要 sudo strace。

4. 大量子进程的处理

对于 Python、Java、Node.js 这种多线程/多进程程序,用 -f 追踪子进程:

# 追踪主进程及其所有子线程/子进程
strace -f -p 12345

但注意 Java 的 JVM 有大堆线程,-f 会导致输出暴涨。建议用 -f -c 先看统计,再用 -f -e trace=xxx 具体过滤。

5. strace 输出乱码

# 如果看到 \x00\x01 之类的,加 -s 控制字符串显示长度
strace -s 4096 -v -p 12345

-v 是 verbose 模式,显示更详细的参数信息。

实战场景

场景一:程序启动卡住,找它卡在哪里

一个新服务启动时卡住没反应,日志也不写,用 strace 看:

# 找进程 PID
ps aux | grep myapp

# 追踪进程
sudo strace -t -T -p PID

输出里会显示它卡在哪个 syscall。常见的卡住原因:
- connect() 很久没返回 = 连接远程服务超时
- open() 返回 ENOENT 且重试 = 配置文件路径不对
- read() 一直阻塞 = 读管道或 socket 没数据
- stat() 反复调用同一个不存在的路径 = 在等待文件出现

场景二:找配置文件被哪个路径读了

很多应用会从多个位置尝试加载配置,失败后又去下一个地方找,导致启动慢:

# 追踪读文件操作,看配置到底从哪读的
sudo strace -e trace=open,openat,stat -s 256 -p PID 2>&1 | grep "\.conf"

输出会显示程序尝试 open("/etc/myapp/config.conf") 等操作,如果返回 ENOENT 说明没找到,返回 FD 数字说明成功打开了。

场景三:断网时观察网络行为

# 应用连不上数据库,看它怎么处理的
sudo strace -e trace=network -t -T -p PID

你会看到:

connect(3, {sa_family=AF_INET, sin_port=htons(3306), sin_addr=inet_addr("10.0.0.5")}, 16) = -1 ETIMEDOUT (Connection timed out)

这说明连接超时了。或者:

connect(3, ...) = -1 EINPROGRESS
poll([{fd=3, events=POLLOUT}], 1, 30000) = 1
...

EINPROGRESS 说明是非阻塞连接,poll 在等连接完成。如果 poll 返回 0 就是超时了。

场景四:诊断性能瓶颈

程序突然变慢,用 strace 统计看哪个 syscall 最耗时间:

# 统计 10 秒内的系统调用分布
sudo strace -c -p PID &
sleep 10
kill %1

或者用时间戳看各阶段耗时:

# 追踪文件 IO 并显示耗时
sudo strace -e trace=read,write,open,close -t -T -p PID

看到 stat() 或 open() 耗时明显,可能是磁盘 IO 瓶颈。看到 read() 经常返回很少字节,可能是小文件太多。

场景五:排查文件描述符泄漏

# 看进程打开文件和关闭文件的比例
sudo strace -e trace=open,openat,close -c -p PID

如果 open 的调用次数远大于 close,说明文件描述符在泄漏,需要检查程序逻辑。

今日作业

你的 Nginx 突然变得很慢,每个请求响应时间从 50ms 飙升到 5 秒。你用 strace 排查性能问题,要求:

1. 找到 Nginx 的 worker 进程 PID
2. 用 strace 统计 30 秒内的系统调用分布
3. 找出最耗时的 3 个系统调用
4. 如果看到 open() 频繁失败(ENOENT),说明什么?
5. 如果看到 write() 经常阻塞很久,说明什么?

写出完整的 strace 命令,并根据输出结果给出修复建议。

E N D

运维少年 · 科技观察

最新文章

随机文章