Python: ulimit и хорошо для subprocess.call / subprocess.Popen?
Мне нужно ограничить количество времени и ресурсов процессора внешними приложениями командной строки, которые я порождаю из процесса python с использованием subprocess.call, главным образом потому, что иногда порожденный процесс застревает и закрепляет процессор на 99%.
nice и ulimit кажутся разумными способами сделать это, но я не уверен, как они будут взаимодействовать с подпроцессом.
- Границы выглядят примерно так:
- Убить процесс, если он занимает больше 60 секунд
- Ограничить до 20% процессорного времени
- Я хочу применить ограничение ресурсов к подпроцессу, а не к процессу Python, который порождает подпроцессы.
Есть ли способ применить nice и ulimit к порожденному процессу subprocess.call? Есть ли лучшие альтернативы Python-native?
Это в системе Linux (Ubuntu).
3 ответа
Вы можете установить ограничения для подпроцессов с помощью ulimit
а также nice
Команды оболочки, как это:
import subprocess
subprocess.Popen('ulimit -t 60; nice -n 15 cpuhog', shell=True)
Это работает cpuhog
с ограничением в 60 секунд процессорного времени и корректностью настройки 15. Обратите внимание, что не существует простого способа установить 20% -ый дроссель процессора как таковой. Процесс будет использовать 100% CPU, если только другой (менее приятный) процесс также нуждается в CPU.
Используйте параметр preexec_fn для subprocess.Popen и модуля ресурсов. Пример:
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(), а затем exec 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, так как не всегда хорошая идея порождать подпроцесс через оболочку, тем более что это часто вызывает неприятные проблемы с цитированием параметров.
Эрик сделал это легко для меня, но он забыл 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()
Вилле использовал shell=True
на что у меня почему-то аллергия. Возможно, я просто старый и сварливый здесь, но я стараюсь избегать этого!