Was passiert unter Linux, wenn ein Programm (das möglicherweise mehrere Threads hat) ein Signal wie SIGTERM oder SIGHUP empfängt?
Welcher Thread fängt das Signal ab? Können mehrere Threads dasselbe Signal erhalten? Gibt es einen speziellen Thread, der ausschließlich dem Umgang mit Signalen gewidmet ist? Wenn nicht, was passiert innerhalb des Threads, der das Signal verarbeiten soll? Wie wird die Ausführung fortgesetzt, nachdem die Signalbehandlungsroutine beendet ist?
pthreads(7)
beschreibt, dass POSIX.1 alle Threads in einem Prozess Share-Attribute erfordert, einschließlich:
POSIX.1 erfordert auch einige Attribute unterscheidbar für jeden Thread, einschließlich:
Die des Linux-Kernels complete_signal
Routine hat den folgenden Codeblock – die Kommentare sind sehr nützlich:
/*
* Now find a thread we can wake up to take the signal off the queue.
*
* If the main thread wants the signal, it gets first crack.
* Probably the least surprising to the average bear.
*/
if (wants_signal(sig, p))
t = p;
else if (!group || thread_group_empty(p))
/*
* There is just one thread and it does not need to be woken.
* It will dequeue unblocked signals before it runs again.
*/
return;
else {
/*
* Otherwise try to find a suitable thread.
*/
t = signal->curr_target;
while (!wants_signal(sig, t)) {
t = next_thread
if (t == signal->curr_target)
/*
* No thread needs to be woken.
* Any eligible threads will see
* the signal in the queue soon.
*/
return;
}
signal->curr_target = t;
}
/*
* Found a killable thread. If the signal will be fatal,
* then start taking the whole group down immediately.
*/
if (sig_fatal(p, sig) &&
!(signal->flags & SIGNAL_GROUP_EXIT) &&
!sigismember(&t->real_blocked, sig) &&
(sig == SIGKILL || !p->ptrace)) {
/*
* This signal will be fatal to the whole group.
*/
Das sieht man also Sie sind dafür verantwortlich, wohin die Signale geliefert werden:
Wenn Ihr Prozess eine Signaldisposition gesetzt hat SIG_IGN
oder SIG_DFL
dann wird das Signal für alle Threads ignoriert (oder standardmäßig — kill, core oderignore).
Wenn Ihr Prozess die Disposition eines Signals auf eine bestimmte Behandlungsroutine festgelegt hat, können Sie steuern, welcher Thread die Signale empfängt, indem Sie bestimmte Thread-Signalmasken mit manipulieren pthread_sigmask(3)
. Sie können einen Thread ernennen, um sie alle zu verwalten, oder einen Thread pro Signal oder eine beliebige Mischung dieser Optionen für bestimmte Signale erstellen, oder Sie verlassen sich auf das aktuelle Standardverhalten des Linux-Kernels, das Signal an den Haupt-Thread zu liefern.
Einige Signale sind jedoch speziell nach der signal(7)
Manpage:
Ein Signal kann für einen Prozess als Ganzes generiert (und somit anstehend) werden (z. B. wenn es mit gesendet wird töten (2)) oder für einen bestimmten Thread (z. B. sind bestimmte Signale, wie etwa SIGSEGV und SIGFPE, die als Folge der Ausführung einer bestimmten Maschinensprachanweisung erzeugt werden, Thread-gerichtet, ebenso wie Signale, die auf eine bestimmte Thread-Verwendung abzielen
pthread_kill(3)). Ein prozessgerichtetes Signal kann an jeden der Threads geliefert werden, bei dem das Signal derzeit nicht blockiert ist. Wenn mehr als einer der Threads das Signal entsperrt hat, wählt der Kernel einen beliebigen Thread, an den er das Signal liefert.
Dies ist leicht nuanciert, je nachdem, welche Version des Linux-Kernels Sie verwenden.
Unter der Annahme von 2.6 Posix-Threads und wenn Sie über das Betriebssystem sprechen, das SIGTERM oder SIGHUP sendet, wird das Signal an den Prozess gesendet, der vom Root-Thread empfangen und verarbeitet wird. Mit POSIX-Threads können Sie SIGTERM auch an einzelne Threads senden, aber ich vermute, Sie fragen, was passiert, wenn das Betriebssystem das Signal an den Prozess sendet.
In 2.6 bewirkt SIGTERM, dass untergeordnete Threads “sauber” beendet werden, während in 2.4 untergeordnete Threads in einem unbestimmten Zustand gelassen wurden.