你的代码有很多问题。首先,这个:
def command_is_running(pid):
with open('/proc/%d/stat' % pid) as stat:
stats = stat.read()
return ' R ' in stats
def wait_command_processes_line(pid):
# stats = ...
while command_is_running(pid):
# stats = update_stats(stats, pid)
return stats
是一个繁忙的循环。它会吃掉尽可能多的 CPU,.../stat
反复读取直到R
消失。当您尝试获得 CPU 使用的准确时间时,运行额外的 CPU 占用进程并不是一个好主意。
在另一个进程的运行状态发生变化之前,我不知道有什么方法可以让一个进程进入睡眠状态,所以我无法为繁忙的循环提供有效的替代方案。但这并不重要,因为第二个问题:进程状态并不像您希望的那样可预测。
您已经假设该进程将在您将一些数据写入其管道的那一刻变得可运行,并且将在处理该输入的持续时间内保持可运行状态。很难保证这是真的。您已经说过“磁盘 IO 很少见”,但您必须做得更好并完全消除它,包括页面错误。这很难,你可能还没有做到。所以我认为你的问题不是/proc/PID/stat
包含错误的信息,而是你在错误的时间阅读它。
您可以通过将D
状态视为与R
. 但它看起来仍然很笨拙。
与其查看进程的可运行性,不如找到一个更好的指标,表明子进程已完成对最近输入行的处理。你说它“可能会打印一些东西到标准输出”。如果您可以安排它始终为每个输入行打印一些内容到标准输出,那么父进程可以等待该输出并在子进程出现时对子进程的 CPU 使用情况进行采样。
如果您无法让子进程为每个输入行提供完成的外部指示,则另一种方法可能是在尝试读取下一个输入行时考虑使用输入行完成。基本上,您将习惯于ptrace
实现一个专门的类似strace
实用程序,记录read
输入管道上 s 的时间,仅在您的跟踪告诉您它正在尝试读取之后才将一行写入管道。
也许你甚至可以使用strace
一些聪明的 shell 脚本来做到这一点。
这个想法的另一个变体是gdb
在子进程的输入处理循环开始时设置一个断点,并设置一个脚本在每次断点被命中时运行。该脚本将收集计时信息,将下一行写入管道,然后执行 gdb cont
。