processi pitone si blocca a SIGTERM / SIGINT dopo essere riavviato
Domanda
Sto avendo un problema strano con alcuni processi in esecuzione pitone utilizzando un processo cane da guardia.
Il processo di watchdog è scritto in Python ed è il genitore, ed ha una funzione chiamata start_child (nome) che usa subprocess.Popen per aprire il processo figlio. L'oggetto Popen è registrato in modo che il watchdog può monitorare il processo utilizzando poll () e poi finire con terminate () quando necessario. Se il bambino muore inaspettatamente, le chiamate watchdog start_child (nome) di nuovo e registra il nuovo oggetto Popen.
Ci sono 7 processi figli, che sono tutti anche pitone. Se corro nessuno dei figli manualmente, posso inviare SIGTERM o SIGINT utilizzando kill e ottenere i risultati mi aspetto (il processo termina).
Tuttavia, quando eseguito dal processo di watchdog, il bambino finirà solo dopo che il primo segnale. Quando il cane da guardia riavvia il bambino, il nuovo processo figlio non risponde più al SIGTERM o SIGINT. Non ho idea di che cosa sta causando questo.
watchdog.py
class watchdog:
# <snip> various init stuff
def start(self):
self.running = true
kids = ['app1', 'app2', 'app3', 'app4', 'app5', 'app6', 'app7']
self.processes = {}
for kid in kids:
self.start_child(kid)
self.thread = threading.Thread(target=self._monitor)
self.thread.start()
while self.running:
time.sleep(10)
def start_child(self, name):
try:
proc = subprocess.Popen(name)
self.processes[name] = proc
except:
print "oh no"
else:
print "started child ok"
def _monitor(self):
while self.running:
time.sleep(1)
if self.running:
for kid, proc in self.processes.iteritems():
if proc.poll() is not None: # process ended
self.start_child(kid)
Quindi, quello che succede è watchdog.start () lancia tutti i 7 processi, e se io mando qualsiasi SIGTERM processo, finisce, e il thread di monitoraggio inizia di nuovo. Tuttavia, se poi inviare la nuova SIGTERM processo, l'ignora.
dovrei essere in grado di tenere l'invio di uccidere -15 ai processi rinnovate più e più volte. Perché ignorano dopo essere stato riavviato?
Soluzione
Come spiegato qui: http://blogs.gentoo.org/agaffney/ 2005/03/18 / python_sucks , quando Python crea un nuovo thread, blocca tutti i segnali per quel filo (e per tutti i processi che genera filo).
Ho fissato questo usando sigprocmask, chiamato attraverso ctypes. Questo può o non può essere il modo "corretto" per farlo, ma funziona.
Nel processo figlio, durante __init__
:
libc = ctypes.cdll.LoadLibrary("libc.so")
mask = '\x00' * 17 # 16 byte empty mask + null terminator
libc.sigprocmask(3, mask, None) # '3' on FreeBSD is the value for SIG_SETMASK
Altri suggerimenti
Non sarebbe meglio per ripristinare i gestori di segnale di default all'interno di Python, piuttosto che tramite ctypes? Nel vostro processo figlio, utilizzare il modulo di segnalazione:
import signal
for sig in range(1, signal.NSIG):
try:
signal.signal(sig, signal.SIG_DFL)
except RuntimeError:
pass
RuntimeError viene generato quando si cerca di impostare segnali quali SIGKILL che non può essere catturato.