Installing signal handler with Python

2019-06-04 23:53发布

问题:

(there is a follow up to this question here)

I am working on trying to write a Python based Init system for Linux but I'm having an issue getting signals to my Python init script. From the 'man 2 kill' page:

The only signals that can be sent to process ID 1, the init process,  are  those for which init has explicitly installed signal handlers.

In my Python based Init, I have a test function and a signal handler setup to call that function:

def SigTest(SIG, FRM):
    print "Caught SIGHUP!"

signal.signal(signal.SIGHUP, SigTest)

From another TTY (the init script executes sh on another tty) if I send a signal, it is completely ignored and the text is never printed. kill -HUP 1

I found this issue because I wrote a reaping function for my Python init to reap its child processes as they die, but they all just zombied, it took awhile to figure out Python was never getting the SIGCHLD signal. Just to ensure my environment is sane, I wrote a C program to fork and have the child send PID 1 a signal and it did register.

How do I install a signal handler the system will acknowledge if signal.signal(SIG, FUNC) isn't working?

Im going to try using ctypes to register my handler with C code and see if that works, but I rather a pure Python answer if at all possible.

Ideas?

( I'm not a programmer, Im really in over my head here :p )

Test code below...

import os
import sys
import time
import signal


def SigTest(SIG, FRM):
    print "SIGINT Caught"

print "forking for ash"
cpid = os.fork()
if cpid == 0:
    os.closerange(0, 4)
    sys.stdin = open('/dev/tty2', 'r')
    sys.stdout = open('/dev/tty2', 'w')
    sys.stderr = open('/dev/tty2', 'w')
    os.execv('/bin/ash', ('ash',))

print "ash started on tty2"

signal.signal(signal.SIGHUP, SigTest)

while True:
    time.sleep(5.0)

回答1:

Signal handlers mostly work in Python. But there are some problems. One is that your handler won't run until the interpreter re-enters it's bytecode interpreter. if your program is blocked in a C function the signal handler is not called until it returns. You don't show the code where you are waiting. Are you using signal.pause()?

Another is that if you are in a system call you will get an exception after the singal handler returns. You need to wrap all system calls with a retry handler (at least on Linux).

It's interesting that you are writing an init replacement... That's something like a process manager. The proctools code might interest you, since it does handle SIGCHLD.

By the way, this code:

import signal

def SigTest(SIG, FRM):
    print "SIGINT Caught"

signal.signal(signal.SIGHUP, SigTest)

while True:
    signal.pause()

Does work on my system.