2012-07-26 17 views
87

Was passiert in Linux, wenn ein Programm (das möglicherweise mehrere Threads hat) ein Signal empfängt, wie SIGTERM oder SIGHUP?Signalverarbeitung mit mehreren Threads unter Linux

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 mit dem Signal umgehen soll? Wie wird die Ausführung fortgesetzt, nachdem die Signalroutine abgeschlossen ist?

Antwort

30

Dies ist leicht nuanciert, basierend auf der Version des Linux-Kernels, den Sie verwenden.

Angenommen, 2.6 Posix-Threads, und wenn Sie sprechen über das Betriebssystem senden SIGTERM oder SIGHUP, wird das Signal an den Prozess gesendet, die von Root-Thread empfangen und behandelt 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 wird SIGTERM dazu führen, dass untergeordnete Threads "sauber" beendet werden, wobei untergeordnete Threads wie in 2.4 in einem unbestimmten Zustand belassen wurden.

+0

Und was im Wurzelfaden passiert, wenn ein Signal empfangen wird? Nehmen wir an, ich habe einen benutzerdefinierten Signal-Handler für SIGUSR1 geschrieben, und jetzt sende ich dieses Signal an den Prozess. Der Root-Thread wird dieses Signal erhalten. Vielleicht ist es gerade in der Mitte einer Funktion. Was wird passieren? –

+1

Wenn Sie ein Handler-Setup haben, wird es als Interrupt behandelt, und der Programmfluss wird angehalten und Ihr benutzerdefinierter Handler wird ausgeführt. Sobald es ausgeführt wird, kehrt die Steuerung zurück, vorausgesetzt, Sie haben nichts getan, um den normalen Ablauf zu ändern (Beenden usw.). – Alan

+0

Beachten Sie, dass dies für SIGUSR1 spezifisch ist, wobei IIRC Systemaufrufe nicht unterbricht. Wenn Sie dies beispielsweise mit SIGINT versuchten, konnte es einen Stream-Lesevorgang unterbrechen, und wenn Sie zum Lesen zurückkehren, gibt der Stream möglicherweise einen Fehler zurück, der unterbrochen wurde. – Alan

102

pthreads(7) beschreibt, dass POSIX.1 alle Threads in einem Prozess Teilen Attributen erfordert, einschließlich:

  • Signal Dispositionen erfordert

POSIX.1 auch einige Attribute deutliche für jeden Thread zu sein, einschließlich:

  • Signalmaske (pthread_sigmask(3))

  • alternative Signalstapel (sigaltstack(2))

Der complete_signal() Routine des Linux-Kernels hat den folgenden Code-Block - 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_UNKILLABLE | SIGNAL_GROUP_EXIT)) && 
     !sigismember(&t->real_blocked, sig) && 
     (sig == SIGKILL || !t->ptrace)) { 
      /* 
      * This signal will be fatal to the whole group. 
      */ 

Also, Sie sehen, dass Sie sind verantwortlich für, wo Signale geliefert werden:

Wenn Sie Prozess hat die Signalverteilung auf SIG_IGN oder SIG_DFL festgelegt, dann wird das Signal für alle Threads ignoriert (oder default - kill, core oder ignore).

Wenn Ihr Prozess die Signalverteilung 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 nominieren oder einen Thread pro Signal oder eine Mischung dieser Optionen für bestimmte Signale erstellen oder Sie verlassen sich auf das aktuelle Standardverhalten des Linux-Kernels, das Signal an den Hauptthread zu liefern.

Einige Signale sind jedoch besondere:

A signal may be generated (and thus pending) for a process as 
    a whole (e.g., when sent using kill(2)) or for a specific 
    thread (e.g., certain signals, such as SIGSEGV and SIGFPE, 
    generated as a consequence of executing a specific machine- 
    language instruction are thread directed, as are signals 
    targeted at a specific thread using pthread_kill(3)). A 
    process-directed signal may be delivered to any one of the 
    threads that does not currently have the signal blocked. If 
    more than one of the threads has the signal unblocked, then 
    the kernel chooses an arbitrary thread to which to deliver 
    the signal. 
+6

Danke, dass Sie sich die Mühe gemacht haben, den Kernel-Code zu extrahieren! sehr hilfreich. – cheshirekow