2. strace 基础:系统调用追踪的瑞士军刀
strace 是 Linux 系统中最常用的系统调用追踪工具。它能够拦截并记录进程发起的每一次系统调用及其返回值,是理解程序行为、定位性能瓶颈和排查故障的第一道防线。本章将深入剖析 strace 的工作原理、核心用法与输出解读,为后续与 perf 联用打下坚实基础。
2.1 什么是 strace
strace(System Trace)是一个用户空间工具,它通过内核提供的 ptrace 系统调用,以“观察者”模式附着到目标进程上,捕获该进程与内核交互的所有系统调用(syscall)以及接收到的信号(signal)。
核心价值: strace 让你“看见”程序与内核的每一次对话——文件读写、网络连接、内存分配、进程创建等,从而诊断“程序为什么慢”、“为什么卡住”或“为什么报错”。
2.2 工作原理:ptrace 系统调用
strace 的底层依赖是 ptrace 系统调用。其工作流程可概括为以下步骤:
- 附着(Attach): strace 通过
ptrace(PTRACE_ATTACH, ...)附着到目标进程,使 strace 成为该进程的“追踪者”(tracer),目标进程成为“被追踪者”(tracee)。 - 暂停与通知: 被追踪进程每次执行系统调用前(进入 syscall)和系统调用返回后(退出 syscall),内核都会暂停该进程,并将控制权转交给 strace。
- 捕获与记录: strace 读取进程的寄存器与内存,获取系统调用号、参数以及返回值,并按照指定格式输出。
- 恢复执行: strace 通过
ptrace(PTRACE_SYSCALL, ...)让被追踪进程继续运行,直到下一次系统调用。
性能代价: 由于每次系统调用都会触发两次上下文切换(进入和退出),strace 会使目标进程的运行速度显著下降(通常 10x~100x)。在生产环境中使用时需格外谨慎。
2.3 基本用法
strace 的命令行语法非常灵活,以下是几种最常用的启动方式:
| 场景 | 命令示例 | 说明 |
|---|---|---|
| 追踪新启动的命令 | strace ls -l |
直接运行 ls -l 并追踪其所有系统调用 |
| 附着到运行中的进程 | strace -p 12345 |
附着到 PID 为 12345 的进程,实时追踪 |
| 追踪指定系统调用 | strace -e trace=open,read,write ./app |
只追踪 open、read、write 三个调用 |
| 追踪网络相关调用 | strace -e trace=network curl http://example.com |
使用 network 集合追踪 socket 相关调用 |
| 统计系统调用耗时 | strace -c -p 12345 |
按系统调用类型统计次数、耗时和错误数 |
| 输出到文件 | strace -o trace.log ./app |
将追踪结果写入 trace.log 文件 |
生产环境警告: 使用
-p 附着到生产进程时,务必确认该进程可以接受性能下降。建议先使用 -c 模式做轻量统计,再决定是否进行详细追踪。
2.4 输出格式解析
strace 的默认输出格式非常规整,每一行代表一次系统调用。以下是一个典型的输出示例:
openat(AT_FDCWD, "/etc/passwd", O_RDONLY) = 3
read(3, "root:x:0:0:root:/root:/bin/bash\n"..., 4096) = 74
write(1, "root:x:0:0:root:/root:/bin/bash\n", 31) = 31
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f1234560000
输出格式的通用结构为:
系统调用名称(参数列表) = 返回值
各字段含义如下:
| 字段 | 示例 | 说明 |
|---|---|---|
| 系统调用名称 | openat |
调用的内核函数名,现代 Linux 多用 openat 替代 open |
| 参数列表 | AT_FDCWD, "/etc/passwd", O_RDONLY |
传递给系统调用的参数,strace 会尝试将常量(如 O_RDONLY)转换为可读符号 |
| 返回值 | = 3 |
系统调用的返回结果。正数通常表示文件描述符或写入字节数;-1 表示出错,此时 errno 会附加在行尾 |
| 错误信息 | = -1 ENOENT (No such file or directory) |
当返回值为 -1 时,strace 会自动显示 errno 符号和描述 |
2.5 常用选项速查表
以下表格汇总了 strace 最实用的选项,便于日常查阅:
| 选项 | 功能 | 典型用法 |
|---|---|---|
-f |
追踪子进程(fork/vfork/clone) | strace -f -o trace.log ./server |
-tt |
显示带微秒精度的时间戳 | strace -tt -p 12345 |
-T |
显示每次系统调用的耗时(微秒) | strace -T -c ./app |
-v |
显示完整参数(不缩写结构体) | strace -v -e trace=ioctl ./app |
-s |
设置字符串显示的最大长度(默认 32) | strace -s 1024 ./app |
-y |
显示文件描述符对应的路径 | strace -y -p 12345 |
-z |
只显示成功返回的系统调用 | strace -z -p 12345 |
-Z |
只显示失败的系统调用 | strace -Z -p 12345 |
2.6 实战技巧与避坑指南
- 优先使用
-e trace=过滤: 不要无脑追踪所有系统调用,指定感兴趣的类别(file、network、process)可以大幅减少输出噪音。 - 结合
-c做快速诊断: 先运行strace -c -p PID观察系统调用分布,找出高频调用或高耗时调用,再针对性地使用-e trace=深入分析。 - 注意
-f的副作用: 追踪多线程/多进程程序时,-f会追踪所有子进程,输出量可能爆炸式增长。建议配合-o输出到文件,避免终端卡死。 - 使用
-y提升可读性: 当看到read(5, ...)时,加上-y可以显示5对应的实际路径(如/var/log/syslog),极大提升调试效率。 - 区分“慢”与“多”: 一个系统调用耗时 1ms 但被调用 10000 次,总耗时 10s;另一个调用耗时 100ms 但只调用 1 次。使用
-T和-c结合分析,才能准确定位瓶颈。
学习路径建议: 本章掌握了 strace 的基础用法后,第 3 章将深入讲解如何通过 strace 输出识别性能瓶颈模式,第 4 章开始引入 perf 进行采样级分析,最终实现 strace + perf 的联合诊断。