I need to limit the amount of time and cpu taken by external command line apps I spawn from a python process using subprocess.call , mainly because sometimes the spawned process gets stuck and pins the cpu at 99%.
nice and ulimit seem like reasonable ways to do this, but I'm not sure how they'd interact with subprocess.
Is there a way to apply nice and ulimit to the subprocess.call spawned process? Are there better python-native alternatives?
This is on a linux (ubuntu) system.
Popen is more general than subprocess. call . Popen doesn't block, allowing you to interact with the process while it's running, or continue with other things in your Python program. The call to Popen returns a Popen object.
The subprocess module defines one class, Popen and a few wrapper functions that use that class. The constructor for Popen takes arguments to set up the new process so the parent can communicate with it via pipes. It provides all of the functionality of the other modules and functions it replaces, and more.
Description. Python method popen() opens a pipe to or from command. The return value is an open file object connected to the pipe, which can be read or written depending on whether mode is 'r' (default) or 'w'. The bufsize argument has the same meaning as in open() function.
Popen is nonblocking. call and check_call are blocking. You can make the Popen instance block by calling its wait or communicate method.
Use the preexec_fn parameter to subprocess.Popen, and the resource module. Example:
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 will fork into a new process. In the new process, it will call setlimits(), then exec child.py. This means the resource will be limited in the child process, but not in the parent.
Output when running program:
./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)
This is in many cases a better solution than trying to use ulimit, since it's not always a good idea to spawn subprocess via shell, especially since it often causes ugly parameter quoting trouble.
You can set limits for subprocesses with the ulimit
and nice
shell commands like this:
import subprocess subprocess.Popen('ulimit -t 60; nice -n 15 cpuhog', shell=True)
This runs cpuhog
with a limit of 60 seconds of CPU time and a niceness adjustment of 15. Note that there is no simple way to set a 20% CPU throttle as such. The process will use 100% CPU unless another (less nice) process also needs the CPU.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With