登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  文章 >  linux

Linux strace -f 如何定位子进程继承的文件访问:follow-forks、文件过滤与证据核对

来源:17golang原创

时间:2026-08-30 05:08:12 282浏览 收藏

排查 Linux 服务“明明没有读这个文件”时,先别只盯着主进程的一行日志:程序可能已经 fork 出子进程,真正的 openat、read 或 stat 发生在另一个 PID 里。最小可用的定位命令是 strace -f -e trace=%file -p PID,其中 -f 扩大跟踪范围,-e trace=%file 把输出收窄到文件相关调用。

先用 -f 保证子进程不会从视野里消失,再用 %file 过滤调用,最后同时核对 PID、路径和返回值;只看到一条“访问失败”并不能证明访问者是主进程。

要点速览
  • strace -f 会跟踪 fork、vfork、clone 产生的进程,输出中的 PID 是判断调用归属的第一证据。
  • -e trace=%file 适合从大量系统调用中筛出 openat、stat、readlink 等文件路径相关调用。
  • -o 保存原始记录,再结合返回值和子进程退出时间核对,避免把“路径不存在”和“权限拒绝”混成一个结论。

接口目标:把“谁访问了文件”还原出来

strace 记录的是进程发起的系统调用,不是应用日志。对一个会派生 worker 的服务来说,目标不是收集越多越好,而是回答三个问题:哪个 PID 发起了调用、调用针对什么路径、内核返回了什么结果。

下面用一个假设的服务 worker 说明命令形状。PID 请替换为现场通过 pgrep -n worker 找到的真实值;不要直接复制示例数字。

sudo strace -f -e trace=%file -p 2418 -o /tmp/worker-file.trace

这里的 2418 只是示例主 PID。跟踪开始后,再触发一次可复现的文件读取动作,随后按 Ctrl+C 结束。生产环境先确认权限、输出文件所在磁盘空间和业务窗口,避免把长期跟踪当成常驻日志。

调用方需求:为什么必须保留 fork 后的子进程

没有 -f 时,strace 只观察附着的那个进程。若主进程随后调用 forkclone,文件访问可能出现在子进程,主进程的记录就会在最关键的位置断开。

-f 后,输出通常会把不同 PID 写在每行开头。下面这段是核对格式的示意,路径和返回值来自本文的验证思路,不代表某台机器的固定结果:

[pid 2418] clone(...) = 2427
[pid 2427] openat(AT_FDCWD, "/srv/worker/config.json", O_RDONLY) = 3
[pid 2427] read(3, "{...}", 4096) = 128
[pid 2427] close(3) = 0

第一行说明主进程创建了子进程,后面三行的 2427 说明实际文件调用属于子进程。这里的连线关系是 2418 产生 2427,再由 2427 发起 openat;不要用主进程 PID 替代调用行前的 PID。

Linux strace -f 从主进程 2418 到子进程 2427,再到 openat 文件访问的调用链

参数设计:用 trace=%file 缩小文件访问范围

完整跟踪会混入信号、内存映射、线程同步等调用。只关心路径和文件描述符时,可以先用系统调用集合过滤:

sudo strace -f -e trace=%file -p 2418 -o /tmp/worker-file.trace

%file 是 strace 提供的系统调用类选择器,实际展开集合由 strace 版本和平台定义决定。它不是“只匹配某个文件名”的过滤器,所以仍要在输出里搜索目标路径:

rg -n 'config\.json|No such file|Permission denied' /tmp/worker-file.trace

这一步的证据链是 trace=%file 先减少噪声,config.json 再筛目标,最后看调用返回值。若路径包含空格或正则元字符,调整 rg 模式,不要误把搜索表达式当成 strace 的系统调用过滤规则。

Linux strace trace=%file 从文件调用集合过滤到 config.json 路径核对的证据链

错误处理:路径出现了,不等于文件读取成功

判断结果要看返回值。= 3 表示 openat 返回了文件描述符,= -1 ENOENT 表示目标不存在,= -1 EACCES 则更接近权限或访问控制问题。它们都可能出现在同一个路径旁边,但修复动作完全不同。

记录片段先得出的结论下一步
openat(...) = 3打开成功继续核对 readclose
= -1 ENOENT路径在该时刻不存在核对工作目录、挂载点和创建时序
= -1 EACCES访问被拒绝核对 UID、目录执行权限和安全策略日志

如果输出中同时出现主进程和多个子进程,先按 PID 分组,再按时间顺序阅读。不要只截取包含文件名的一行,因为前一行可能是子进程创建,后一行可能是关闭或错误回退。

兼容策略:附着、启动跟踪和输出文件怎么选

已运行服务适合用 -p PID 附着;如果问题只在启动阶段出现,应该让 strace 启动目标程序:

strace -f -e trace=%file -o /tmp/worker-start.trace -- ./worker --config /srv/worker/config.json

附着模式能减少重启影响,但可能错过启动期的文件访问;启动模式能覆盖最早的调用,却会改变启动时序。两种方式都用 -o 保存原始记录,便于把 PID、路径和返回值交给同事复核。

最小核验示例:从一行输出走到可复查结论

拿到 /tmp/worker-file.trace 后,按下面顺序核验:

  1. 先看每行的 PID,确认调用者是 2418 还是由它产生的 2427。
  2. 再看 openat 的路径是否确实是 /srv/worker/config.json,不要只凭文件名判断。
  3. 最后看返回值,再决定是检查文件存在性、权限,还是继续追踪后续 read

这套顺序把“进程归属”“路径匹配”“内核结果”拆开了。即便最终没有找到目标路径,也能保留一份可复查的负证据:在本次触发和筛选条件下,哪个进程没有发起预期调用。

常见问题

为什么 strace 里看不到子进程的文件访问?

最常见原因是附着时没有使用 -f,或者子进程在跟踪开始前已经创建。启动期问题优先用启动跟踪,运行期问题再用带 -f 的附着方式复测。

-e trace=%file 会不会只显示某一个文件?

不会。它选择的是文件相关系统调用集合,目标路径仍需要在输出文件中搜索;要确认是否成功,还必须阅读返回值。

看到 ENOENT 就能确定文件不存在吗?

只能说明该调用当时解析到的路径没有对应对象。还要核对进程的工作目录、挂载命名空间、符号链接和调用发生的时刻。

总结

定位 Linux 子进程文件访问时,strace -f 解决的是跟踪范围,-e trace=%file 解决的是输出噪声,-o 解决的是证据保存。真正的结论来自三项同时成立:调用行的 PID、完整路径和系统调用返回值。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>