2009-06-21 8 views
3

Ich schreibe ein systemkritisches Programm für eine Linux-Distribution, die ich entwickle. Es muss sich beim Empfang bestimmter Signale neu starten, um einen Absturz zu vermeiden. Das Problem ist, dass ich das Signal nach dem Neustart nicht wieder aktivieren kann. Das heißt, das Signal kann nicht zweimal empfangen werden. Wenn der neue Prozess signal() aufruft, um das Signal einzurichten, wird SIG_DFL zurückgegeben. Jedes Mal. Auch wenn ich es zweimal hintereinander rufe - was bedeutet, dass es nie an erster Stelle gesetzt wurde. Wird eine seltsame Flagge vom ursprünglichen Prozess übernommen?Signale werden über execv nicht korrekt wieder aktiviert()

Antwort

8

Sie fallen mit der Tatsache, dass Sie im Wesentlichen versuchen, ein Signal rekursiv umzugehen.

Wenn ein Signalhandler mit signal() registriert wird, wird diese Signalnummer blockiert, bis der Signalhandler zurückkehrt - tatsächlich blockiert der Kernel/libc diese Signalnummer, wenn der Signalhandler aufgerufen wird, und entsperrt ihn nach Rückkehr des Signalhandlers. Da du nie wieder vom Signalhandler zurückkehrst (statt eine neue Binärdatei), bleibt SIGUSR1 blockiert und wird somit beim 2. Mal nicht abgefangen.

Dies kann durch die Prüfung /proc/</pid>/status vor und nach dem Senden der ersten SIGUSR1 gesehen werden.

Bevor:

$ cat /proc/<pid>/status | grep -E "Sig(Cgt|Blk)" 
SigBlk: 0000000000000000 
SigCgt: 0000000000000200 

After:

$ cat /proc/<pid>/status | grep -E "Sig(Cgt|Blk)" 
SigBlk: 0000000000000200 
SigCgt: 0000000000000200 

anzumerken, dass SigCgt Signal zeigt an 10 registriert ist (die Anzahl ist ein Bitfeld; 10. Bit gesetzt ist, die zu SIGUSR1 entspricht, siehe man signal(7) für die Zahlen). SigBlk ist leer, bevor SIGUSR an Ihren Prozess gesendet wird, aber nach dem Senden des Signals enthält es SIGUSR1.

Sie haben zwei Möglichkeiten, dies zu lösen:

a). entsperren SIGUSR manuell vor execl in sighandler Aufruf:

sigset_t sigs; 
sigprocmask(0, 0, &sigs); 
sigdelset(&sigs, SIGUSR1); 
sigprocmask(SIG_SETMASK, &sigs); 

b). Verwenden Sie sigaction mit dem SA_NODEFER Flag anstelle von signal, um den Signalhandler zu registrieren. Dadurch wird verhindert, dass SIGUSR1 innerhalb des Signalhandlers blockiert wird:

struct sigaction act; 
act.sa_handler = signalhandler; 
act.sa_mask = 0; 
act.sa_flags = SA_NODEFER; 
sigaction(SIGUSR1, &act, 0); 
+0

Ich entdeckte, dass ich es entsperren musste - ich nahm idiotisch an, dass es nicht blockierte. Jedenfalls habe ich NODEFER versucht, und das hat nicht funktioniert, aber es im Handler entsperrt, bevor der Exec Call funktioniert. Vielen Dank. – c4757p

1

Signal-Handler werden nicht über exec weil exec überschreibt die gesamte Adressraum, und alle Signal-Handler geerbt, die nicht würde zurückgesetzt dann an der falschen Stelle zeigen. Die einzige Zeit, zu der es nicht zurückgesetzt wird, ist, wenn es beispielsweise auf SIG_IGN gesetzt ist, was nicht von dem Adressraum des Prozesses vor dem exec abhängt.

+0

Das ist, was ich denke. Was ich sagte, ist, dass der neue Prozess nach der Exekution * nicht * das Signal wieder setzen kann - nicht, dass er nicht übertragen wird. Egal, was ich mache, nachdem ich einen Prozess von einem Signal-Handler ausgeführt habe, kann ich das gleiche Signal nicht noch einmal einstellen. – c4757p

+0

Haben Sie einen Code zur Hand, mit dem ich testen kann? Ich bin jetzt wirklich neugierig darauf. –

+0

Sicher. Versuchen Sie '-10' zu töten. Sie können es nur einmal tun - es ignoriert alle nachfolgenden Versuche. http://www.box.net/shared/tgfaef5l8i – c4757p

Verwandte Themen