Python: subprocess.call / subprocess.Popen中的ulimit和nice?
我想限制从 Python 进程中启动的外部命令行应用程序所占用的时间和 CPU,主要是因为有时候这些启动的进程会卡住,导致 CPU 使用率达到 99%。
使用 nice 和 ulimit 似乎是合理的办法,但我不太确定它们和 subprocess 的关系。
- 限制的内容大致如下:
- 如果进程运行超过 60 秒,就杀掉它
- 限制它的 CPU 使用率在 20% 以内
- 我想把这些资源限制应用到子进程上,而不是应用到启动子进程的 Python 进程上。
有没有办法把 nice 和 ulimit 应用到 subprocess.call 启动的进程上?有没有更好的 Python 原生替代方案?
这是在一个 Linux(Ubuntu)系统上。
3 个回答
Erik 给我提供了很好的帮助,但他忘记了Rich 提到的 nice
部分。我觉得 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
,对此我有点过敏。也许我只是年纪大了,有点挑剔,但我尽量避免使用它!
使用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来创建子进程并不总是一个好主意,尤其是这常常会导致参数引号的问题。
你可以通过 ulimit
和 nice
这两个命令来给子进程设置一些限制,方法如下:
import subprocess
subprocess.Popen('ulimit -t 60; nice -n 15 cpuhog', shell=True)
这段代码会让 cpuhog
这个程序运行时,CPU使用时间限制在60秒内,并且调整它的优先级为15。需要注意的是,没有简单的方法可以直接设置20%的CPU使用限制。也就是说,这个进程会使用100%的CPU,除非有其他优先级更低的进程也需要使用CPU。