第 6 章 · 08 系统调用追踪:`strace`、`ltrace`


文档摘要

第 6 章 · 08 系统调用追踪: 、 本节摘要:这是本章的「终极武器」。当你把 、 、 都试过了,还是搞不清「程序为什么卡住」「它到底在读哪个文件」「为什么连不上」,就该上 了。它能打印进程的每一个系统调用(system call)——open/read/write/connect/futex...,等于给程序装了「行车记录仪」。本节要讲清 的三个核心用法(直接跑、attach 运行中进程、过滤特定调用),以及它的代价(性能开销大,只在必要时用)。 是排查疑难杂症的最后手段,也是深入理解「程序与内核如何交互」的窗口。 内容来源:原项目「Linux 命令大全」 ,精选并套用体系化模板。

第 6 章 · 08 系统调用追踪:straceltrace

本节摘要:这是本章的「终极武器」。当你把 pstoplsof 都试过了,还是搞不清「程序为什么卡住」「它到底在读哪个文件」「为什么连不上」,就该上 strace 了。它能打印进程的每一个系统调用(system call)——open/read/write/connect/futex...,等于给程序装了「行车记录仪」。本节要讲清 strace 的三个核心用法(直接跑、attach 运行中进程、过滤特定调用),以及它的代价(性能开销大,只在必要时用)。strace 是排查疑难杂症的最后手段,也是深入理解「程序与内核如何交互」的窗口。

内容来源:原项目「Linux 命令大全」command/strace.md,精选并套用体系化模板。

学习目标

阅读完本节,你应当能够:

  1. 说清「系统调用(system call)」是什么:用户程序请求内核服务的接口(open/read/write/connect 等)。
  2. 理解 strace 的工作原理:拦截并打印进程的每个系统调用及其参数、返回值。
  3. strace 命令 直接跑并追踪、用 strace -p PID attach 运行中的进程。
  4. -e trace=open,read 过滤只看特定调用,过滤掉噪音。
  5. -f 跟踪子进程(fork 出来的)。
  6. 识别 strace 输出里的「卡住信号」(某调用长时间不返回)。
  7. 说清 strace 的性能代价,懂得它是「最后手段」而非日常工具。

一、设计动机:系统调用是「程序与内核的边界」

程序跑起来时,大部分时间在用户态执行自己的代码,但当它需要「内核才能做的事」时——读文件、收发网络、创建进程、分配内存——就发起一次系统调用(system call),陷入内核态,内核做完再把结果返回给程序。

strace 利用内核的 ptrace 机制,拦截进程的每次系统调用,打印调用名、参数、返回值:

open("/etc/config", O_RDONLY) = 3 # 打开文件, 得到 fd 3 read(3, "key=value\n", 4096) = 10 # 从 fd 3 读到 10 字节 close(3) = 0 # 关闭 fd 3 connect(4, {sa_family=AF_INET, port=80, ...}, 16) = -1 ECONNREFUSED # 连接被拒

从这段输出你能看出:程序打开了 /etc/config、读了内容、然后试图连接 80 端口但被拒绝了(ECONNREFUSED)——「为什么连不上」的答案就在这里。这种「程序与内核交互的全程记录」,是 strace 独有的能力,别的工具给不了。

二、高频组合与实战

直接跑并追踪

strace ./myapp # 追踪 myapp 的所有系统调用(输出很多) strace -o trace.log ./myapp # 输出到文件, 不污染终端

attach 运行中的进程

strace -p 1234 # attach 到正在跑的进程 1234 # 按 Ctrl+C 停止追踪(不会杀掉被追踪的进程)

这是排查「已经在跑的服务突然卡住」的标准操作——不重启它,直接 attach 看。

过滤噪音(最实用)

原始 strace 输出量巨大(每个 read/write 都打印),必须过滤:

strace -e trace=open,openat ./myapp # 只看文件打开 strace -e trace=network ./myapp # 只看网络相关 strace -e trace=connect ./myapp # 只看连接(排查连不上) strace -e trace=process ./myapp # 只看进程相关(fork/exec) strace -e trace=file ./myapp # 只看文件操作

跟踪子进程

strace -f ./myapp # -f: 也跟踪 fork 出来的子进程

不加 -f,子进程的系统调用不会被追踪——这是排查「主进程没事但子进程卡住」时的常见坑。

看时间(排查卡在哪)

strace -T ./myapp # 显示每个调用的耗时(秒) strace -tt ./myapp # 显示每个调用的时间戳(微秒级) strace -r ./myapp # 显示相邻调用的相对时间(找"停顿"在哪)

某条调用后面的时间特别长(比如 read(3, ...) = 10 <5.234>),就是它卡了 5 秒——直接定位「卡在哪个系统调用」。

三、实战场景:经典排查模式

场景 1:程序卡住,不知卡在哪

strace -p <PID> -T # attach, 看时间 # 如果输出停在某一行不动, 那一行就是它在等的调用 # 常见: read(...) 不返回(等网络/输入)、futex(... 等锁

场景 2:程序报「找不到文件」但没说哪个

strace -e trace=open,openat ./myapp 2>&1 | grep -i ENOENT # 看哪些 open 返回 -1 ENOENT(文件不存在), 精确定位"它在找哪个文件"

场景 3:程序连不上数据库/网络

strace -e trace=connect ./myapp # 看 connect 的返回值: ECONNREFUSED(被拒)、ETIMEDOUT(超时)、EHOSTUNREACH(不可达)

四、踩坑与排错

坑 1:性能开销巨大

strace 让每个系统调用都多一次 ptrace 拦截,程序会慢 5-50 倍。绝对不要在生产环境对高负载服务长时间 attach——会让服务雪崩。排查完立即 Ctrl+C 脱离。

坑 2:不加过滤,输出淹没你

strace ./myapp | head -100 # 前几屏都是 read/write 的字节流, 找不到关键信息 strace -e trace=open ./myapp # 一定要过滤!

坑 3:忘了 -f 漏掉子进程

strace ./server # 主进程没事, 但主进程 fork 的 worker 卡了, 你看不到! strace -f ./server # 加 -f 才能看到子进程

坑 4:ltrace 是另一个工具

strace 追踪系统调用(程序→内核);ltrace 追踪库函数调用(程序→动态库,如 malloc/printf)。两者层次不同,排查「内存泄漏(看 malloc/free)」用 ltrace 更合适,排查「卡在 IO/网络」用 strace。

本节要点回顾

  1. 系统调用是程序请求内核服务的接口;strace 打印进程的每个系统调用及其参数、返回值,是程序的「行车记录仪」。
  2. 三大用法:strace 命令(直接跑)、strace -p PID(attach 运行中进程)、-e trace=xxx(过滤特定调用)。
  3. -f 跟踪子进程(不加会漏掉 fork 出来的进程);-T/-tt 显示时间(找「卡在哪」)。
  4. 经典排查模式:卡住(attach 看停在哪)、找不到文件(过滤 open 看 ENOENT)、连不上(过滤 connect 看返回值)。
  5. 性能开销大(慢 5-50 倍),是「最后手段」而非日常工具;生产环境慎用,排查完立即脱离。
  6. strace(系统调用)vs ltrace(库函数):排查 IO/网络用 strace,排查内存泄漏用 ltrace。

本章「进程、作业与性能观测」全部讲完。从 ps/top 看进程,到信号与 kill 控制进程,到 free/vmstat 观测系统,再到 lsof/strace 深度排查——你已拥有了一套从「看见」到「读懂」到「控制」到「终极诊断」的完整进程排查工具链。下一章我们把视角从「本机进程」延伸到「网络连通」,继续扩展这套工具链。


发布者: 作者: 灏天文库 转发
评论区 (0)
U