暗色模式

strace 系统调用追踪:从卡死进程到缺失文件定位

技术教程
2026-08-16
8
0
本文要点
  • strace 跟踪进程与内核之间的一次次系统调用(open/read/write/connect 等),是"程序在底层到底干了什么"的照妖镜
  • 基本用法 strace <命令> 直接跟踪命令执行;-f 跟随子进程,-o 输出到文件避免刷屏
  • -e trace=openat,write 只过滤关心的调用类型;返回值 -1 ENOENT 能精准定位"找不到文件/配置"类故障
  • strace -p 附着到已运行的进程,排查"进程卡住/假死"时直接看它阻塞在哪个系统调用上
  • strace -c 输出系统调用统计表(次数/耗时占比),快速判断程序的调用热点

为什么需要 strace:程序与内核之间的一层"监控摄像头"

平时排查 Linux 问题时,我们能看到的都是程序"愿意展示"的:日志、退出码、报错信息。但当程序卡死、崩溃、或者"悄悄读了一个不存在的文件"时,日志往往帮不上忙。strace 提供的是另一层视角:程序每一次进入内核的请求(打开文件、读写数据、建立连接、等待信号……)都叫系统调用,strace 会把它们逐个记录下来,包括调用的参数和内核返回的结果。

换句话说,普通工具看到的是"程序说了什么",strace 看到的是"程序实际上做了什么"。很多疑难杂症(启动即崩、卡住不动、读了错误路径的配置)在系统调用层面往往一眼就能看出来。

Ubuntu 上安装很简单:

apt install strace

基本用法:strace 跟踪一个命令

直接在命令前加上 strace,程序跑的过程中产生的所有系统调用都会打到标准错误输出。不过全量跟踪会很吵(启动一个命令会伴随大量加载动态库、读取 locale 等系统调用),所以实际排查时常用 -e trace= 只过滤关心的类型。先看过滤后的效果:

strace -e trace=openat,write cat /etc/hostname

strace 跟踪 cat 打开并输出文件

输出里每行就是一个系统调用,格式是 调用名(参数) = 返回值

  • openat(AT_FDCWD, "/etc/hostname", O_RDONLY) = 3cat 以只读方式打开 /etc/hostname,内核返回文件描述符 3
  • write(1, "demo\n", 5) = 5:把读到的 demo 内容写到文件描述符 1(标准输出),写了 5 个字节;
  • 前面还有一堆 openat(...) = -1 ENOENT (No such file or directory),是程序在探测系统里的 locale 文件路径,属于正常"尝试"行为,不是错误——判断一次调用是否真的失败,要看它是否影响了你关心的路径
  • 结尾 +++ exited with 0 +++ 表示进程以退出码 0 正常结束。

过滤:-e trace= 只关心特定调用

全量跟踪默认会输出几十上百行。真实场景下我们往往只关心某一类系统调用,-e trace= 支持按类过滤:

strace -e trace=file cat /etc/hostname        # 所有文件类调用
strace -e trace=network nc -z 1.1.1.1 443     # 网络类调用
strace -e trace=signal ./server               # 信号相关
strace -e trace=read,write ./app              # 只关心读写
strace -e trace=process ls                    # 进程创建/执行类

常用类别有 filenetworkprocesssignalmemorydesc(文件描述符相关),也可以直接写具体调用名如 -e trace=openat,或用逗号组合 -e trace=openat,write。跟踪时加上 -f 会同时跟随命令 fork/exec 出的子进程(比如 shell 脚本里的每一条命令),-o 文件名 则把输出写到文件,避免在屏幕上刷屏:

strace -f -o /tmp/trace.log bash -c 'echo hello; cat /etc/hostname'

实战:程序读不到配置文件,strace 一眼定位

典型故障场景:程序报"找不到配置",但你怎么都查不出它到底在找哪个路径。用 strace 过滤文件类调用跑一次,看它实际尝试打开过哪些路径即可:

strace -e trace=openat cat /etc/absent.conf

strace 定位缺失文件 ENOENT

关键两行:

  • openat(AT_FDCWD, "/etc/absent.conf", O_RDONLY) = -1 ENOENT (No such file or directory):程序尝试打开 /etc/absent.conf,内核返回 -1,错误码 ENOENT(没有这个文件);
  • cat: /etc/absent.conf: No such file or directory+++ exited with 1 +++cat 把错误转成报错信息,进程以退出码 1 结束。

遇到类似问题,把 -e trace=openat 的输出里 = -1 ENOENT 的行找出来,就能确认程序"到底想要哪个文件、缺在哪个路径",而不是去猜。这对那种把配置路径写进编译参数、默认去多个候选目录找的程序尤其有效。

附着到已运行的进程:strace -p 排查"卡死"

程序已经跑起来并卡住了,没法重跑,怎么办?strace -p <PID> 可以附着到正在运行的进程上,实时观察它后续执行的系统调用。先 pgrep 找到 PID:

strace -p $(pgrep -f 'sleep 100') -e trace=nanosleep

附着后输出格式一样,比如 nanosleep({tv_sec=100,...}) = ? 会显示进程正阻塞在 nanosleep(睡眠)调用上;如果卡在 readconnect 上,就能确认是等待输入还是网络连接超时。这是排查"服务假死/无响应"最直接的手段:

strace -p $(pgrep nginx) -e trace=network   # 看 nginx 当前网络调用
strace -p $(pgrep -f 'java.*app') -e trace=read,write,connect  # Java 进程卡顿排查

-p 同时只能附着一个进程(可用 -p PID1,PID2 附着多个),结束后按 Ctrl+C 脱离,进程本身不受影响,会继续正常运行。

统计热点:strace -c 看系统调用次数与耗时

想快速了解一个程序"忙在哪里",用 -c 汇总统计而不是逐行输出:

strace -c ls /var/log

strace -c 系统调用统计

统计表按耗时占比排序,每列含义:

  • % time:该系统调用占用总时间的百分比;
  • seconds:累计耗时(秒);
  • calls:调用次数;
  • errors:出错次数(比如 ENOENT);
  • syscall:调用名。

对于性能问题,先看耗时占比最高的几个调用,再针对性深入;如果 calls 巨大,则可能是日志刷屏、轮询过密等。strace -c 只做概要统计,比逐行跟踪开销低得多,适合先做整体扫描。

strace 的常见坑与替代工具

几个容易踩的注意点:

  • 跟踪会显著降低性能:进程每一次系统调用都要被截获记录,对高频 IO 程序可能拖慢几十倍,生产环境慎用、用完即摘;
  • 会改变时序:附着本身可能改变竞态条件,偶现 bug 可能"一跟踪就消失";
  • 看不到用户态内部逻辑:strace 只记录内核边界的调用,函数内部计算需要调试器(如 gdb)或 ltrace(跟踪库函数调用);
  • 权限限制:附着他人进程需要相同用户或 root 权限。

类似场景的工具还有 ltrace(跟踪动态库函数)、perf trace(更高效的内核跟踪,适合生产环境)、以及 eBPF 系的 bpftrace,可作为深入排查的下一步。

小结

strace 的价值在于把"程序行为"还原成一条条可见的系统调用:-e trace= 过滤、-p 附着运行进程、-c 统计热点,三个用法覆盖了故障定位最常见的三类场景——找不到文件、进程卡住、性能热点。遇到日志解释不了的问题时,先跑一次 strace,往往比继续猜更接近真相。

发表评论

暂无评论,快来抢沙发吧!