Python: subprocess.call / subprocess.Popen中的ulimit和nice?

48 投票
3 回答
34049 浏览
提问于 2025-04-15 15:45

我想限制从 Python 进程中启动的外部命令行应用程序所占用的时间和 CPU,主要是因为有时候这些启动的进程会卡住,导致 CPU 使用率达到 99%。

使用 nice 和 ulimit 似乎是合理的办法,但我不太确定它们和 subprocess 的关系。

  • 限制的内容大致如下:
    • 如果进程运行超过 60 秒,就杀掉它
    • 限制它的 CPU 使用率在 20% 以内
  • 我想把这些资源限制应用到子进程上,而不是应用到启动子进程的 Python 进程上。

有没有办法把 nice 和 ulimit 应用到 subprocess.call 启动的进程上?有没有更好的 Python 原生替代方案?

这是在一个 Linux(Ubuntu)系统上。

3 个回答

11

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,对此我有点过敏。也许我只是年纪大了,有点挑剔,但我尽量避免使用它!

121

使用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来创建子进程并不总是一个好主意,尤其是这常常会导致参数引号的问题。

12

你可以通过 ulimitnice 这两个命令来给子进程设置一些限制,方法如下:

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

这段代码会让 cpuhog 这个程序运行时,CPU使用时间限制在60秒内,并且调整它的优先级为15。需要注意的是,没有简单的方法可以直接设置20%的CPU使用限制。也就是说,这个进程会使用100%的CPU,除非有其他优先级更低的进程也需要使用CPU。

撰写回答