How to cleanly kill subprocesses in python
There are 2 main issues here:
First issue: If you're using shell=True
, so you're killing the shell running the process, not the process itself. With its parent killed, the child process goes defunct / isn't killed immediately.
In your case, you're using sleep
which is not built-in, so you could drop shell=True
, and Popen
would yield the actual process id: p.terminate()
would work.
You can (and you should) avoid shell=True
most of the time, even if it requires extra python coding effort (piping 2 commands together, redirecting input/output, all those cases can be nicely handled by one or several Popen
without shell=True
.
And (second issue) if the process is still defunct when terminating after that fix, you could call p.wait()
(from this question). Seems that calling terminate
isn't enough. The Popen
object needs to be garbage collected.
How to terminate a python subprocess launched with shell=True
Use a process group so as to enable sending a signal to all the process in the groups. For that, you should attach a session id to the parent process of the spawned/child processes, which is a shell in your case. This will make it the group leader of the processes. So now, when a signal is sent to the process group leader, it's transmitted to all of the child processes of this group.
Here's the code:
import os
import signal
import subprocess
# The os.setsid() is passed in the argument preexec_fn so
# it's run after the fork() and before exec() to run the shell.
pro = subprocess.Popen(cmd, stdout=subprocess.PIPE,
shell=True, preexec_fn=os.setsid)
os.killpg(os.getpgid(pro.pid), signal.SIGTERM) # Send the signal to all the process groups
How to kill a subprocess in python
Hope it will help you.
import os
import signal
import subprocess
process = subprocess.Popen(cmd, shell = True)
os.killpg(os.getpgid(process.pid), signal.SIGTERM)
Ensuring subprocesses are dead on exiting Python program
You can use atexit for this, and register any clean up tasks to be run when your program exits.
atexit.register(func[, *args[, **kargs]])
In your cleanup process, you can also implement your own wait, and kill it when a your desired timeout occurs.
>>> import atexit
>>> import sys
>>> import time
>>>
>>>
>>>
>>> def cleanup():
... timeout_sec = 5
... for p in all_processes: # list of your processes
... p_sec = 0
... for second in range(timeout_sec):
... if p.poll() == None:
... time.sleep(1)
... p_sec += 1
... if p_sec >= timeout_sec:
... p.kill() # supported from python 2.6
... print 'cleaned up!'
...
>>>
>>> atexit.register(cleanup)
>>>
>>> sys.exit()
cleaned up!
Note -- Registered functions won't be run if this process (parent process) is killed.
The following windows method is no longer needed for python >= 2.6
Here's a way to kill a process in windows. Your Popen object has a pid attribute, so you can just call it by success = win_kill(p.pid) (Needs pywin32 installed):
def win_kill(pid):
'''kill a process by specified PID in windows'''
import win32api
import win32con
hProc = None
try:
hProc = win32api.OpenProcess(win32con.PROCESS_TERMINATE, 0, pid)
win32api.TerminateProcess(hProc, 0)
except Exception:
return False
finally:
if hProc != None:
hProc.Close()
return True
Related Topics
Unsatisfiableerror While Installing Miniconda
How to Do Multiple Processes for Django, on My Wsgi Apache
Convert a Gtk Python Script to C
How to Return a Value from a Shell Script in a Python Script
What Does -≫ Mean in Python Function Definitions
Getting a List of All Subdirectories in the Current Directory
Do Not Want the Images to Load and CSS to Render on Firefox in Selenium Webdriver - Python
How to Find the Mountpoint a File Resides On
Command 'X86_64-Linux-Gnu-Gcc' Failed with Exit Status 1
Python's Equivalent for R's Dput() Function
What Conditions Result in an Opened, Nonblocking Named Pipe (Fifo) Being "Unavailable" for Reads
Why Does Simple Echo in Subprocess Not Working
How to Lock a Directory Between Python Processes in Linux
Kill Python Interpeter in Linux from the Terminal
Install Tkinter and Python Locally
(Still) Cannot Properly Install Lxml 2.3 for Python, But at Least 2.2.8 Works