Python:subprocess.call / subprocess.Popen的ulimit和nice参数是什么?

46

我需要限制从Python进程使用subprocess.call生成的外部命令行应用程序所占用的时间和CPU数量,主要是因为有时候生成的进程会被卡住并将CPU占用率固定在99%。

nice和ulimit似乎是实现这一目的的合理方式,但我不确定它们如何与subprocess相互作用。

  • 限制大致如下:
    • 如果进程运行超过60秒,则杀死该进程
    • 将CPU利用率限制为20%
  • 我希望将资源限制应用于子进程,而不是生成子进程的Python进程。

是否有一种方法可以将nice和ulimit应用于由subprocess.call生成的子进程?是否有更好的Python本地替代方案?

这是在Linux(Ubuntu)系统上进行的。


3
你可能想要接受得票最高的答案,而不是我的答案。它比我的好得多。 - Ville Laurikari
3个回答

120

使用subprocess.Popen的preexec_fn参数和resource模块。示例:

parent.py:

#!/usr/bin/env python

import os
import sys
import resource
import subprocess

def setlimits():
    # Set maximum CPU time to 1 second in child process, after fork() but before exec()
    print "Setting resource limit in child (pid %d)" % os.getpid()
    resource.setrlimit(resource.RLIMIT_CPU, (1, 1))

print "CPU limit of parent (pid %d)" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU)
p = subprocess.Popen(["./child.py"], preexec_fn=setlimits)
print "CPU limit of parent (pid %d) after startup of child" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU)
p.wait()
print "CPU limit of parent (pid %d) after child finished executing" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU)

child.py:

#!/usr/bin/env python

import os
import sys
import resource

print "CPU limit of child (pid %d)" % os.getpid(), resource.getrlimit(resource.RLIMIT_CPU)

parent.py会分叉出一个新进程。在新进程中,它将调用setlimits(),然后执行child.py。这意味着资源将在子进程中受到限制,但在父进程中不受限制。

运行程序时的输出:

./parent.py
CPU limit of parent (pid 17404) (-1, -1)
Setting resource limit in child (pid 17405)
CPU limit of parent (pid 17404) after startup of child (-1, -1)
CPU limit of child (pid 17405) (1, 1)
CPU limit of parent (pid 17404) after child finished executing (-1, -1)

在许多情况下,这比尝试使用ulimit更好,因为通过shell生成子进程并不总是一个好主意,特别是由于它经常会引起丑陋的参数引用问题。


3
是的,资源包通过setrlimit对Python进程设置了限制,但在我的示例中,它对通过subproces.Popen创建的子进程设置了限制,在调用exec()运行子进程之前设置。因此,在这个示例中,调用进程的限制不受影响,只有子进程的限制受到影响。 - Erik Forsberg
谢谢您提供的解决方案,很有道理。但在这种情况下,我只需要限制子进程 - 父进程实际上需要保持不受限制。 - Parand
7
这个答案是100%正确的,应该被选为正确答案。 - Michael van der Westhuizen
4
对资源限制的回答很好,但它没有涉及nice命令的使用。 - RichVel
3
答案应该添加 os.nice。 - W.Mann
显示剩余8条评论

12

您可以使用ulimitnice shell命令设置子进程的限制,如下所示:

import subprocess
subprocess.Popen('ulimit -t 60; nice -n 15 cpuhog', shell=True)

这将以60秒的CPU时间限制和15的优先级调整运行cpuhog。请注意,没有简单的方法来设置20%的CPU限制。该进程将使用100%的CPU,除非另一个(较不友好的)进程也需要CPU。


谢谢Ville,你所描述的CPU限制非常有效。你知道是否可以使用括号语法而不是字符串来指定命令执行相同的操作吗? - Parand
据我所知,对于这样的情况,您必须将整个 shell 命令作为一个字符串传递才能使其正常工作。 - Ville Laurikari
9
这确实不应该被标记为采纳答案的解决方案。结合用户提供的参数,这很容易会导致安全漏洞。 - W.Mann

11

Erik帮助我轻松解决了问题,但他忘记了Rich指出的那个“好”的部分。 我发现psutil软件包很棒(意思是很好用),但不幸的是不太可移植。以下是我的回答:

import os
import psutil
import resource
import subprocess

def preexec_fn():
    pid = os.getpid()
    ps = psutil.Process(pid)
    ps.set_nice(10)
    resource.setrlimit(resource.RLIMIT_CPU, (1, 1))

print "mother pid", os.getpid()
p = subprocess.Popen(["./cpuhog.sh"], preexec_fn=preexec_fn)
p.wait()
print "mother still alive with pid", os.getpid()

Ville使用了shell=True,但我对此有点过敏。也许我只是老气横秋了,但我尽量避免使用它!


5
Python内置的os已经有nice了,为什么还需要psutil呢? - WGH
可能是因为你无法将PID传递给os.nice,但可以传递给psutil - cliff.wells
@cliff.wells,您不需要传递子进程的PID,os.nice更改当前进程的niceness,在运行时它就是子进程。 - Błażej Michalik
@BłażejMichalik 很好的观点。 - cliff.wells

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接