Python 如何在Linux中跟踪所有子进程

Python 如何在Linux中跟踪所有子进程,python,linux,python-multiprocessing,ptrace,bpf,Python,Linux,Python Multiprocessing,Ptrace,Bpf,我正在创建一个需要生成多个进程的库 我希望能够知道测试期间产生的所有子进程的集合。这对于在通过的测试结束时终止行为良好的守护进程或通过获取测试失败后出现的任何进程的堆栈跟踪来调试死锁/挂起进程非常有用 由于其中一些需要生成守护进程(fork、fork,然后让父进程死亡),我们无法通过遍历进程树找到所有进程 目前我的做法是: 使用 在fork的child中,聚集一个文件并将(pid,进程开始时间)附加到另一个文件中 然后,当需要时,我们可以通过迭代文件中的条目,并保持其中的条目(pid,流程开始时

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

我希望能够知道测试期间产生的所有子进程的集合。这对于在通过的测试结束时终止行为良好的守护进程或通过获取测试失败后出现的任何进程的堆栈跟踪来调试死锁/挂起进程非常有用

由于其中一些需要生成守护进程(fork、fork,然后让父进程死亡),我们无法通过遍历进程树找到所有进程

目前我的做法是:

  • 使用
  • 在fork的child中,聚集一个文件并将
    (pid,进程开始时间)
    附加到另一个文件中
  • 然后,当需要时,我们可以通过迭代文件中的条目,并保持其中的条目(pid,流程开始时间)与现有流程匹配来获得子流程集
  • 这种方法的缺点是:

  • 仅适用于
    多处理
    操作系统。fork
    -在使用
    子进程
    或非Python进程生成新Python进程时不起作用
  • 在测试期间锁定分叉可能会使事情比实际情况更具确定性,从而隐藏竞争条件
  • 我正在寻找一种不同的方法来跟踪子进程,以避免这两个缺点

    我考虑过的备选方案:

  • 使用来注册fork/clone的探测——问题是它需要root,我认为从贡献者的角度来看,这对于运行测试来说有点烦人。作为一个非特权用户,是否可以为当前进程和子进程执行类似的操作
  • 使用与上述类似的strace(或ptrace)——问题在于性能影响。有几个测试专门针对启动时间进行基准测试,ptrace的开销相对较大。如果只跟踪fork和clone,情况可能会更糟,但它仍然与在测试超时时获取堆栈的愿望相冲突

  • 有人能提出一种解决这个问题的方法来避免上面提到的陷阱和缺点吗?我现在只对Linux感兴趣,理想情况下,它不需要4.15以上的内核。

    对于
    subprocess.Popen
    ,有
    preexec\u fn
    参数用于可调用——您可以通过它进行黑客攻击


    或者,看看(控制组)——我相信它们可以处理一些棘手的情况,比如守护进程的创建等等。

    对于
    subprocess.Popen
    ,有一个可调用的
    preexec_fn
    参数——你可以通过它来破解


    或者,看看(控制组)——我相信它们可以处理一些棘手的情况,比如守护进程的创建等等。

    考虑到我最初文章中的限制,我使用了以下方法:

  • putenv(“PID\u DIR”)
  • 对于当前进程,使用版本覆盖
    fork
    clone
    ,将进程开始时间跟踪到
    $PID\u DIR/
    。替代使用完成,并应用于所有加载的共享对象<还应重写code>dlopen,以重写任何其他动态加载库上的函数
  • 将实现了
    \uu libc\u start\u main
    fork
    clone
    的库设置为
    LD\u PRELOAD
  • 初始实现可用,如:

    导入过程跟踪程序;进程_tracker.install()
    导入操作系统
    pid1=os.fork()
    pid2=os.fork()
    pid3=os.fork()
    如果pid1、pid2和pid3:
    打印(进程\u tracker.children())
    
    考虑到我的原始帖子的限制,我采用了以下方法:

  • putenv(“PID\u DIR”)
  • 对于当前进程,使用版本覆盖
    fork
    clone
    ,将进程开始时间跟踪到
    $PID\u DIR/
    。替代使用完成,并应用于所有加载的共享对象<还应重写code>dlopen,以重写任何其他动态加载库上的函数
  • 将实现了
    \uu libc\u start\u main
    fork
    clone
    的库设置为
    LD\u PRELOAD
  • 初始实现可用,如:

    导入过程跟踪程序;进程_tracker.install()
    导入操作系统
    pid1=os.fork()
    pid2=os.fork()
    pid3=os.fork()
    如果pid1、pid2和pid3:
    打印(进程\u tracker.children())
    
    谢谢,preexec\u fn填补了我上面提到的一个空白。我读到的关于cGroup的所有内容似乎都表明,您需要获得特权或进行一些事先设置,以允许非特权进程使用它。不幸的是,是的,因为它们用于控制系统资源,例如CPU共享或进程组的内存限制。谢谢,preexec_fn弥补了我上面提到的一个漏洞。我所读到的关于cgroup的所有内容似乎都表明,您需要获得特权,或者进行一些预先设置,以允许非特权进程使用它。不幸的是,是的,因为它们用于控制系统资源,例如CPU共享或进程组的内存限制。您是否可以通过查找第一个进程的进程组中的所有内容来跟踪情况,或者子进程是否更改了其pgrp?子进程是否更改了其进程组。我尝试了ptrace方法,但不喜欢这样一个事实,即我不能轻松地在测试中的进程上使用strace或gdb,因此我想我可能会创建一个带有
    \uu libc\u start\u main
    垫片的库,并在测试开始时设置
    LD\u PRELOAD
    ,以便所有子进程都选择它。我相信我关心的所有流程都是动态链接的,所以它应该涵盖我的用例。你能通过查找第一个流程的流程组中的所有内容来跟踪事情吗,或者子流程是否会更改它们的pgrp