python - 如何在 Linux 中跟踪所有后代进程
问题描述
我正在制作一个需要生成多个进程的库。
我希望能够知道在测试期间产生的所有后代进程的集合。这对于在通过测试结束时终止表现良好的守护程序或通过获取失败测试后存在的任何进程的堆栈跟踪来调试死锁/挂起进程很有用。
由于其中一些需要生成守护进程(fork,fork,然后让父进程死亡),我们无法通过遍历进程树来找到所有进程。
目前我的方法是:
- 使用注册处理程序
os.register_at_fork
- 在 fork 中,在 child 中,聚集一个文件并附
(pid, process start time)
加到另一个文件中 - 然后在需要时,我们可以通过迭代文件中的条目并保持(pid,进程开始时间)与现有进程匹配的条目来获取子进程集
这种方法的缺点是:
- 仅适用于
multiprocessing
或os.fork
- 在使用subprocess
或非 Python 进程生成新的 Python 进程时不起作用。 - 锁定分叉可能会使测试期间的事情比实际情况更具确定性,从而隐藏竞争条件。
我正在寻找一种不同的方法来跟踪避免这两个缺点的子进程。
我考虑过的替代方案:
- 使用bcc注册 fork/clone 的探针 - 问题是它需要 root,我认为从贡献者的角度运行测试会有点烦人。是否有类似的事情可以作为非特权用户仅用于当前进程和后代?
- 使用与上述类似的 strace(或 ptrace) - 问题在于性能影响。有几个测试专门对启动时间进行基准测试,ptrace 的开销相对较大。如果只跟踪 fork 和 clone 可能会更少,但它仍然与在测试超时时获取堆栈的愿望相冲突。
有人可以建议一种方法来避免上述问题的陷阱和缺点吗?我现在只对 Linux 感兴趣,理想情况下它不需要 4.15 之后的内核。
解决方案
鉴于我原来的帖子的限制,我使用了以下方法:
putenv("PID_DIR", <some tempdir>)
- 对于当前进程,覆盖
fork
和clone
使用将跟踪进程开始时间的版本到$PID_DIR/<pid>
. 覆盖是使用plthook完成的,并适用于所有加载的共享对象。dlopen
还应该重写以重写任何其他动态加载的库上的函数。 - 设置一个具有
__libc_start_main
、fork
和clone
as实现的库LD_PRELOAD
。
此处提供了一个初始实现,如下所示:
import process_tracker; process_tracker.install()
import os
pid1 = os.fork()
pid2 = os.fork()
pid3 = os.fork()
if pid1 and pid2 and pid3:
print(process_tracker.children())
推荐阅读
- javascript - 在 Aframe 运行时加载 3D 对象时如何访问网格?
- python - pd.DateOffet(years=) 在 years>333 处分解
- c# - 如何以编程方式确定 Windows 中是否安装了 Mono 64 位以及安装在何处?
- php - Composer while 'docker-compose' 与挂载绑定
- queue - 算法和数据结构 - 队列
- verilog - 可以将互连解析为结构类型吗?
- linux - 树莓派的 PCM 设备驱动程序
- zend-guard - 无法使用 zend 保护运行编码的 php 文件
- postgresql - utf8 编码中字节序列为 0xe2 0x80 0xa6 的字符在 latin1 编码中没有等价物
- vim - Vim 函数使用传递的参数插入静态文本