processos Python pára de responder a SIGTERM / SIGINT depois de ser reiniciado
Pergunta
Estou tendo um problema estranho com alguns processos python em execução usando um processo de vigilância.
O processo watchdog é escrito em Python e é o pai, e tem uma função chamada start_child (nome) que usa subprocess.Popen para abrir o processo filho. O objeto Popen é gravado para que o cão de guarda pode monitorar o processo usando poll () e, eventualmente, terminar com terminar () quando necessário. Se a criança morre inesperadamente, as chamadas de vigilância start_child (nome) novamente e registros o novo objeto Popen.
Existem 7 processos filhos, os quais são também python. Se eu executar qualquer um dos filhos manualmente, posso enviar SIGTERM ou SIGINT usando kill e obter os resultados que eu esperava (os fins de processo).
No entanto, quando executado a partir do processo de cão de guarda, a criança só vai terminar após o sinal de PRIMEIRO. Quando o cão de guarda reinicia a criança, o novo processo filho já não responde a SIGTERM ou SIGINT. Eu não tenho idéia o que está causando isso.
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)
Então, o que acontece é watchdog.start () lançamentos de todos os 7 processos, e se eu enviar qualquer SIGTERM processo, ele termina, eo thread de monitor começa novamente. No entanto, se eu, em seguida, enviar a nova SIGTERM processo, ignora-lo.
Eu deveria ser capaz de manter o envio de matar -15 aos processos reiniciado uma e outra vez. Por que eles ignorá-lo depois de ser reiniciado?
Solução
Como explicado aqui: http://blogs.gentoo.org/agaffney/ 2005/03/18 / python_sucks , quando Python cria uma nova thread, ele bloqueia todos os sinais para esse segmento (e por quaisquer processos que desova de rosca).
Eu reparei isso usando sigprocmask, chamados através ctypes. Isto pode ou não pode ser a maneira "correta" de fazê-lo, mas ela não funciona.
No processo filho, 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
Outras dicas
Não seria melhor para restaurar os manipuladores de sinais padrão dentro Python em vez de através ctypes? Em seu processo de criança, use o módulo de sinal:
import signal
for sig in range(1, signal.NSIG):
try:
signal.signal(sig, signal.SIG_DFL)
except RuntimeError:
pass
RuntimeError é gerado quando tentando sinais definidos como SIGKILL que não podem ser capturados.