python sigkill catching strategies
SIGKILL by its very nature cannot be trapped.
See http://en.wikipedia.org/wiki/Unix_signal#SIGKILL:
SIGKILL
The SIGKILL signal is sent to a process to cause it to terminate
immediately (kill). In contrast to SIGTERM and SIGINT, this signal cannot
be caught or ignored, and the receiving process cannot perform any
clean-up upon receiving this signal.
The best thing to do is the next time your process launches, look for anything that needs to be cleaned up.
And yes, kill -9 <pid>
will send a SIGKILL to the process. (To be precise, it sends the 9th signal - it just happens that SIGKILL has the number 9 on pretty much every system. You could alternatively write kill -KILL <pid>
, which lets you specify the signal by name instead of by number in a portable way.)
Python script terminated by SIGKILL rather than throwing MemoryError
It sounds like you've run into the dreaded Linux OOM Killer. When the system completely runs of out of memory and the kernel absolutely needs to allocate memory, it kills a process rather than crashing the entire system.
Look in the syslog for confirmation of this. A line similar to:
kernel: [884145.344240] mysqld invoked oom-killer:
followed sometime later with:
kernel: [884145.344399] Out of memory: Kill process 3318
Should be present (in this example, it mentions mysql specifically)
You can add these lines to your /etc/sysctl.conf
file to effectively disable the OOM killer:
vm.overcommit_memory = 2
vm.overcommit_ratio = 100
And then reboot. Now, the original, memory hungry, process should fail to allocate memory and, hopefully, throw the proper exception.
Setting overcommit_memory
means that Linux won't over commit memory, meaning memory allocations will fail if there isn't enough memory for them. See this answer for details on what effect the overcommit_ratio
has: https://serverfault.com/a/510857
How to send SIGINT to python running in a subprocess
Maybe try:
#!/usr/local/cpython-3.8/bin/python3
import signal
import subprocess
import time
proc = subprocess.Popen(['python3', 'B.py'], shell=False)
time.sleep(1)
proc.send_signal(signal.SIGINT)
proc.wait()
...as your A.py?
I suspect the shell is ignoring SIGINT rather than passing it down to B.py.
Killing the children with the parent
prctl's PR_SET_DEATHSIG
can only be set for this very process that's calling prctl -- not for any other process, including this specific process's children. The way the man page I'm pointing to expresses this is "This value is cleared upon a fork()" -- fork
, of course, is the way other processes are spawned (in Linux and any other Unix-y OS).
If you have no control over the code you want to run in subprocesses (as would be the case, essentially, for your gnuchess
example), I suggest you first spawn a separate small "monitor" process with the role of keeping track of all of its siblings (your parent process can let the monitor know about those siblings' pids as it spawns them) and sending them killer signals when the common parent dies (the monitor needs to poll for that, waking up every N seconds for some N of your choice to check if the parent's still alive; use select
to wait for more info from the parent with a timeout of N seconds, within a loop).
Not trivial, but then such system tasks often aren't. Terminals do it differently (via the concept of a "controlling terminal" for a process group) but of course it's trivial for any child to block THAT off (double forks, nohup
, and so on).
Logs from signal handler hidden when redirecting stdout to file via tee
If you don't want this to happen, put tee
in the background so it isn't part of the process group getting a SIGINT
. For example, with bash 4.1 or newer, you can start a process substitution with an automatically-allocated file descriptor providing a handle:
#!/usr/bin/env bash
# ^^^^ NOT /bin/sh; >(...) is a bashism, likewise automatic FD allocation.
exec {log_fd}> >(exec tee log.txt) # run this first as a separate command
python3 -u myfile >&"$log_fd" 2>&1 # then here, ctrl+c will only impact Python...
exec {log_fd}>&- # here we close the file & thus the copy of tee.
Of course, if you put those three commands in a script, that entire script becomes your foreground process, so different techniques are called for. Thus:
python3 -u myfile > >(trap '' INT; exec tee log.txt) 2>&1
Kill or terminate subprocess when timeout?
You could do something like this:
import subprocess as sub
import threading
class RunCmd(threading.Thread):
def __init__(self, cmd, timeout):
threading.Thread.__init__(self)
self.cmd = cmd
self.timeout = timeout
def run(self):
self.p = sub.Popen(self.cmd)
self.p.wait()
def Run(self):
self.start()
self.join(self.timeout)
if self.is_alive():
self.p.terminate() #use self.p.kill() if process needs a kill -9
self.join()
RunCmd(["./someProg", "arg1"], 60).Run()
The idea is that you create a thread that runs the command and to kill it if the timeout exceeds some suitable value, in this case 60 seconds.
SIGKILL to a subprocess tree on parent termination
Creating a subreaper is not useful in this case, your grandchildren would be reparented to and reaped by init
anyway.
What you could do however is:
- Start a parent process and
fork
a child immediately. - The parent will simply
wait
for the child. - The child will carry out all the work of your actual program, including spawning any other children via
fork
+execve
. - Upon exit of the child for any reason (including deathly signals e.g. a crash) the parent can issue
kill(0, SIGKILL)
orkillpg(getpgid(0), SIGKILL)
to kill all the processes in its process group. Issuing aSIGINT
/SIGTERM
beforeSIGKILL
would probably be a better idea depending on what child processes you want to run, as they could handle such signals and do a graceful cleanup of used resources (including children) before exiting.
Assuming that none of the children or grandchildren changes their process group while running, this will kill the entire tree of processes upon exit of your program. You could also keep the PR_SET_PDEATHSIG
before any execve
to make this more robust. Again depending on the processes you want to run a PR_SET_PDEATHSIG
with SIGINT
/SIGTERM
could make more sense than SIGKILL
.
You can issue setpgid(getpid(), 0)
before doing any of the above to create a new process group for your program and avoid killing any parents when issuing kill(0, SIGKILL)
.
The logic of the "parent" process should be really simple, just a fork
+ wait
in a loop + kill
upon the right condition returned by wait
. Of course, if this process crashes too then all bets are off, so take care in writing simple and reliable code.
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
How to Extract a Floating Number from a String
Comparing a String to Multiple Items in Python
How to Get the Full Path of the Current File'S Directory
Get Mouse Deltas Using Python! (In Linux)
Setuid Bit on Python Script:Linux VS Solaris
Detect Log File Rotation (While Watching Log File for Modification)
Find Broken Symlinks with Python
How to Install Lxml for Python Without Administative Rights on Linux
Permission Denied When Executing Python File in Linux
Trying to Simulate Constant Byte Rate. Confusion with Time.Sleep Results
Will Python Systemrandom/Os.Urandom Always Have Enough Entropy for Good Crypto
How to Perform Low Level I/O on a Linux Device File in Python
How to Run Python Script on Usb Flash-Drive Insertion
Python: Get Mount Point on Windows or Linux
List All Currently Open File Handles
Errors When Trying to Save Command Line Output to a File