如何跟踪Linux中的所有后代进程

时间:2019-05-07 21:52:11

标签: python linux python-multiprocessing ptrace bpf

我正在创建一个需要产生多个进程的库。

我想知道测试期间产生的所有后代进程的集合。这对于在通过的测试结束时终止行为良好的守护程序或通过获取测试失败后存在的任何进程的堆栈跟踪信息来调试死锁/挂起进程很有用。

由于其中某些操作需要生成守护程序(叉,叉,然后让父进程死掉),因此我们无法通过遍历进程树来找到所有进程。

目前我的方法是:

  1. 使用os.register_at_fork
  2. 注册处理程序
  3. 在fork上,在子级中,将文件植绒并将(pid, process start time)附加到另一个文件中
  4. 然后在需要时,我们可以通过遍历文件中的条目并保留(pid,进程开始时间)与现有进程匹配的条目来获取子进程集。

这种方法的缺点是:

  1. 仅适用于multiprocessingos.fork-使用subprocess或非Python进程生成新的Python进程时不起作用。
  2. 在测试过程中,用叉子锁起来可能比实际情况更具确定性,从而隐藏了比赛条件。

我正在寻找一种可以避免这两个弊端的跟踪子进程的方法。

我考虑过的替代品:

  1. 使用bcc注册fork / clone的探针-问题在于它需要root用户,从贡献者的角度来看,我认为这对于运行测试会很烦人。作为非特权用户,是否可以针对当前进程和后代执行类似的操作?
  2. 使用与上面类似的strace(或ptrace)-问题在于对性能的影响。其中一些测试专门针对启动时间进行基准测试,而ptrace具有相对较大的开销。如果仅跟踪fork和clone,可能会少一些,但是仍然与在测试超时时获取堆栈的愿望相冲突。

有人可以提出解决该问题的方法,以避免上述方法的弊端和弊端吗?我现在仅对Linux感兴趣,理想情况下,它不应该要求4.15之后的内核。

2 个答案:

答案 0 :(得分:0)

对于subprocess.Popen,有一个preexec_fn参数可调用-您可以通过它来破解自己的方式。

或者,看看cgroups(控制组)-我相信它们可以处理棘手的情况,例如创建守护程序等等。

答案 1 :(得分:0)

鉴于原始帖子的限制,我使用了以下方法:

  1. putenv("PID_DIR", <some tempdir>)
  2. 对于当前流程,请使用版本将forkclone覆盖,这些版本将把流程开始时间追溯到$PID_DIR/<pid>。覆盖是使用plthook完成的,并且适用于所有已加载的共享对象。 dlopen也应该被覆盖,以覆盖任何其他动态加载的库中的功能。
  3. 将实现为__libc_start_mainforkclone的库设置为LD_PRELOAD

here的初始实现可用,其用法如下:

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())