it-swarm.com.de

Signalbehandlung mit mehreren Threads unter Linux

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 das gleiche 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?

111
user500944

Dies ist etwas differenziert, je nachdem, welche Version des Linux-Kernels Sie verwenden.

Unter der Annahme von 2.6 posix-Threads und wenn Sie davon sprechen, dass das Betriebssystem SIGTERM oder SIGHUP sendet, wird das Signal an process gesendet, das vom Root-Thread empfangen und verarbeitet wird. Mit POSIX-Threads können Sie SIGTERM auch an einzelne Threads senden, aber ich vermute, Sie fragen sich, was passiert, wenn das Betriebssystem das Signal an den Prozess sendet.

In 2.6 bewirkt SIGTERM, dass untergeordnete Threads "sauber" beendet werden, während untergeordnete Threads in 2.4 in einem unbestimmten Zustand belassen wurden.

35
Alan

pthreads(7) beschreibt, dass POSIX.1 alle Threads in den Attributen einer Prozessfreigabe erfordert, einschließlich:

  • signal Dispositionen

Für POSIX.1 müssen außerdem einige Attribute für jeden Thread distinct sein, einschließlich:

Die complete_signal - Routine des Linux-Kernels 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(t);
                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.
         */

Sie sehen also, dass Sie dafür zuständig sind, wohin die Signale geliefert werden:

Wenn Ihr Prozess die Disposition eines Signals auf SIG_IGN Oder SIG_DFL Gesetzt hat, wird das Signal für alle Threads ignoriert (oder standardmäßig - Kill, Core oder Ignorieren).

Wenn Ihr Prozess die Disposition eines Signals für eine bestimmte Handler-Routine festgelegt hat, können Sie steuern, welcher Thread die Signale empfängt, indem Sie bestimmte Thread-Signalmasken mit pthread_sigmask(3) bearbeiten. Sie können einen Thread für die Verwaltung aller Threads oder einen Thread pro Signal oder eine beliebige Mischung dieser Optionen für bestimmte Signale festlegen oder sich auf das aktuelle Standardverhalten des Linux-Kernels bei der Übermittlung des Signals an den Haupt-Thread verlassen.

Einige Signale sind jedoch speziell gemäß der Manpage signal(7) :

Ein Signal kann für einen gesamten Prozess (z. B. beim Senden mit kill (2) ) oder für einen bestimmten Thread (z. B. bestimmte Signale wie SIGSEGV und SIGFPE) generiert werden (und damit anstehen) , die als Folge der Ausführung eines bestimmten maschinensprachlichen Befehls erzeugt werden, sind thread-gerichtet, ebenso wie Signale, die mit pthread_kill (3) ) auf einen bestimmten Thread abzielen. Ein prozessgesteuertes Signal kann an einen der Threads gesendet werden, für den das Signal derzeit nicht blockiert ist. Wenn für mehr als einen der Threads das Signal nicht blockiert ist, wählt der Kernel einen beliebigen Thread aus, an den das Signal gesendet werden soll.

123
sarnold