Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send SIGINT to Python subprocess using os.kill as if pressing Ctrl+C

Using python 3.4 on windows.
I am trying to terminate a child processing simulating a person pressing Ctrl+C (Ctrl+D on linux).

I just added the handler to check if the signal was being handled at all. I used the idea from this question

The objective is to catch the KeyboardInterrupt (SIGINT), and release the resources. But it seems that the exception is not thrown if SIGINT doesnt come from the keyboard. That's why I created an handler, but the process doesnt seem to run the handler at all...

import multiprocessing
import time
import signal
import signal
import os
import sys

def handler(signal, frame):
    print("handler!!!")
    sys.exit(10)

def worker(): 
    p = multiprocessing.current_process()
    try:
        signal.signal(signal.SIGINT,handler)  
        print("[PID:{}] acquiring resources".format(p.pid))
        while(True):           
            #working...
            time.sleep(0.5)
    except (KeyboardInterrupt, SystemExit):
        pass
    finally:
        print("[PID:{}] releasing resources".format(p.pid))

if __name__ == "__main__":
    lst = []
    for i in range(1):
        p = multiprocessing.Process(target=worker)
        p.start()
        lst.append(p)

    time.sleep(3)      
    for p in lst:        
        os.kill(p.pid,signal.SIGINT)
        p.join()
        print(p)
        print(p.exitcode)
    print("joined all processes")

Here is an example of the output:

C:\Users\Rui>python test.py
[PID:16524] acquiring resources
<Process(Process-1, stopped[2])>
2
joined all processes
  • What am I doing wrong?
  • Should I use subprocess module?
  • What other approaches could I try to interrupt a process execution?
like image 531
Rui Botelho Avatar asked Sep 29 '22 13:09

Rui Botelho


1 Answers

It's not working because you can't use os.kill to send arbitrary signals on Windows:

os.kill(pid, sig)

Send signal sig to the process pid. Constants for the specific signals available on the host platform are defined in the signal module.

Windows: The signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT signals are special signals which can only be sent to console processes which share a common console window, e.g., some subprocesses. Any other value for sig will cause the process to be unconditionally killed by the TerminateProcess API, and the exit code will be set to sig. The Windows version of kill() additionally takes process handles to be killed.

The only signals that can be sent via os.kill are signal.CTRL_C_EVENT and signal.CTRL_BREAK_EVENT. Anything else just terminates the process, which is what is happening in your case. Using signal.CTRL_C_EVENT won't work here, either, because processes started via multiprocessing.Process aren't "console processes which share a common console window" with the parent. I'm not sure there's much you can do here with signals on Windows; It doesn't look like you're allowed to catch TerminateProcess the way you can catch SIGTERM on Unix, so you can't do any clean up prior to the process terminating, and you're not using a console application for the child, so signal.*_EVENT won't work.

I think your options are: 1) Use the subprocess module, and launch the child process with shell=True, which I believe will mean signal.CTRL+C+EVENT will work. 2) Stick with the multiprocessing module, and use a "co-operative" method of interrupting the worker, like multiprocessing.Event.

like image 119
dano Avatar answered Oct 02 '22 16:10

dano