POSIX-Threads und -Signale

81

Ich habe versucht, die Feinheiten der Interaktion von POSIX-Threads und POSIX-Signalen zu verstehen. Insbesondere interessiert mich:

  • Wie lässt sich am besten steuern, an welchen Thread ein Signal gesendet wird (vorausgesetzt, es ist überhaupt nicht schwerwiegend)?
  • Was ist der beste Weg, um einem anderen Thread (der möglicherweise gerade beschäftigt ist) mitzuteilen, dass das Signal angekommen ist? (Ich weiß bereits, dass es eine schlechte Idee ist, pthread-Bedingungsvariablen von einem Signalhandler zu verwenden.)
  • Wie kann ich sicher damit umgehen, dass die Information, dass ein Signal aufgetreten ist, an andere Threads weitergegeben wird? Muss dies im Signalhandler geschehen? (Ich möchte im Allgemeinen die anderen Threads nicht beenden; ich brauche einen weitaus subtileren Ansatz.)

Als Referenz darüber, warum ich das möchte, untersuche ich, wie das TclX- Paket in Support-Threads konvertiert oder aufgeteilt werden kann und zumindest einige nützliche Teile Threads unterstützen. Signale sind einer der Teile, die von besonderem Interesse sind.

Donal Fellows
quelle

Antworten:

48
  • Wie kann am besten gesteuert werden, an welchen Thread ein Signal gesendet wird?

Wie @ zoli2k angedeutet hat, ist es eine gute Technik, explizit einen einzelnen Thread für alle zu behandelnden Signale (oder eine Reihe von Threads mit jeweils spezifischen Signalverantwortlichkeiten) zu benennen.

  • Was ist der beste Weg, um einem anderen Thread (der möglicherweise gerade beschäftigt ist) mitzuteilen, dass das Signal angekommen ist? [...]
  • Wie kann ich sicher damit umgehen, dass die Information, dass ein Signal aufgetreten ist, an andere Threads weitergegeben wird? Muss dies im Signalhandler geschehen?

Ich werde nicht "am besten" sagen, aber hier ist meine Empfehlung:

Blockieren Sie alle gewünschten Signale main, damit alle Threads diese Signalmaske erben. Stellen Sie dann den speziellen Signalempfangsthread als signalgesteuerte Ereignisschleife her und senden Sie neu angekommene Signale als eine andere Intra-Thread-Kommunikation aus .

Der einfachste Weg, dies zu tun, besteht darin, dass der Thread Signale in einer Schleife mit sigwaitinfoodersigtimedwait akzeptiert . Der Thread konvertiert dann die Signale irgendwie, sendet möglicherweise eine pthread_cond_t, weckt andere Threads mit mehr E / A auf und stellt einen Befehl in eine anwendungsspezifische thread-sichere Warteschlange, was auch immer.

Alternativ könnte der spezielle Thread die Übermittlung von Signalen an einen Signalhandler ermöglichen und die Übermittlung für die Übermittlung nur entlarven, wenn sie bereit sind, Signale zu verarbeiten. (Die Signalübertragung über Handler ist jedoch tendenziell fehleranfälliger als die Signalakzeptanz über die sigwaitFamilie.) In diesem Fall führt der Signalhandler des Empfängers eine einfache und asynchronsignalsichere Aktion aus: Setzen von sig_atomic_tFlags, Aufrufen sigaddset(&signals_i_have_seen_recently, latest_sig), write() eines Bytes zu einer nicht blockierenden Selbstleitung usw. Dann kommuniziert der Thread zurück in seiner maskierten Hauptschleife den Empfang des Signals an andere Threads wie oben.

( UPDATED @caf weist zu Recht darauf hin, dass sigwaitAnsätze überlegen sind.)

Pilger
quelle
1
Dies ist eine viel nützlichere Antwort, zumal sie auch für die Behandlung nicht tödlicher Signale verwendet werden kann. Vielen Dank!
Donal Fellows
1
Am einfachsten ist es, wenn der Signalverarbeitungs-Thread überhaupt keine Signalhandler installiert. Stattdessen wird eine Schleife auf sigwaitinfo()(oder sigtimedwait()) durchlaufen und diese dann wie im letzten Absatz beschrieben an den Rest der Anwendung weitergeleitet.
Café
@caf, in der Tat so. Aktualisiert
Pilcrow
14

Gemäß dem POSIX-Standard sollten alle Threads mit derselben PID im System pthread_sigmask()angezeigt werden. Mit können Sie die Signalblockierungsmaske für jeden Thread definieren.

Da es nur einen Signalhandler pro PID definieren darf, bevorzuge ich es, alle Signale in einem Thread zu verarbeiten und zu senden, pthread_cancel()wenn ein laufender Thread abgebrochen werden muss. Dies ist der bevorzugte Weg, pthread_kill()da damit Bereinigungsfunktionen für die Threads definiert werden können.

Auf einigen älteren Systemen haben die laufenden Threads aufgrund der fehlenden ordnungsgemäßen Kernelunterstützung möglicherweise eine andere PID als die PID des übergeordneten Threads. Siehe FAQ zur Signalverarbeitung mit linuxThreads unter Linux 2.4 .

zoli2k
quelle
In dem, was Sie sagen, bedeutet "implementiert" was? Es ist auch nicht korrekt, andere Threads als Reaktion auf ein Signal immer zu nuklearisieren (SIGHUP und SIGWINCH erfordern mehr Subtilität), und es ist dennoch unsicher, Bedingungsvariablen zu verwenden, um andere Threads zu informieren. Schlechte Antwort.
Donal Fellows
1
Meine Abwahl wurde entfernt, aber es ist immer noch keine ausreichende Antwort, da ich nicht einfach Threads als Antwort auf ein Signal beenden kann. In einigen Fällen werde ich Ereignisse lokal als Antwort in die Warteschlange stellen, in anderen muss ich Threads sehr sorgfältig abreißen (Übrigens, ich habe die meisten Maschinen, um diese Teile bereits zu erledigen; es sind die Verbindungen zum Betriebssystem fehlende Signale).
Donal Fellows
1
@ zoli2k: Ich habe erst kürzlich versucht, make menuconfigmit dem frisch geklonten Git-Master-Zweig von uClibc zu laufen . Es gibt eine Wahl zwischen den alten LinuxThreads und der neueren NPTL als POSIX-Thread-Implementierungen, aber die Hilfe ab dem Jahr 2012 rät immer noch davon ab, NPTL zu wählen. Daher ist es in modernen eingebetteten Linux-Systemen immer noch üblich, dass die veraltete LinuxThreads-Implementierung verwendet wird, selbst wenn auf dem System ein ausreichend aktueller Linux-Kernel ausgeführt wird.
FooF
3

Wo ich bisher bin:

  • Signale kommen in verschiedenen Hauptklassen, von denen einige normalerweise den Prozess sowieso nur abbrechen sollten (SIGILL) und von denen einige nie etwas tun müssen (SIGIO; einfacher, asynchrone E / A-Vorgänge trotzdem richtig auszuführen). Diese beiden Klassen brauchen keine Aktion.
  • Einige Signale müssen nicht sofort verarbeitet werden. SIGWINCH kann in die Warteschlange gestellt werden, bis es zweckmäßig ist (genau wie bei einem Ereignis von X11).
  • Die kniffligen sind diejenigen, bei denen Sie darauf reagieren möchten, indem Sie Ihre Arbeit unterbrechen, ohne jedoch einen Thread auszulöschen. Insbesondere sollte SIGINT im interaktiven Modus die Dinge reaktionsschnell machen.

Ich habe noch zu sortieren bekam signalvs sigaction, pselect, sigwait, sigaltstack, und eine ganze Reihe anderer Stücke von POSIX (und Nicht-POSIX) API.

Donal Fellows
quelle
3

IMHO, Unix V-Signale und Posix-Threads mischen sich nicht gut. Unix V ist 1970. POSIX ist 1980;)

Es gibt Stornierungspunkte, und wenn Sie Signale und pthreads in einer Anwendung zulassen, werden Sie schließlich Schleifen um jeden Anruf schreiben, die überraschenderweise EINTR zurückgeben können.

In den (wenigen) Fällen, in denen ich Multithreading unter Linux oder QNX programmieren musste, habe ich alle Signale für alle (außer einem) Thread ausgeblendet.

Wenn ein Unix V-Signal eintrifft, wechselt der Prozess den Stapel (das war in Unix V so viel Parallelität, wie Sie innerhalb eines Prozesses erreichen konnten).

Wie die anderen Beiträge hier andeuten, könnte es jetzt möglich sein, dem System mitzuteilen, welcher Posix-Thread das Opfer dieses Stapelwechsels sein soll.

Sobald Sie es geschafft haben, Ihren Signal-Handler-Thread zum Laufen zu bringen, bleibt die Frage, wie Sie die Signalinformationen in etwas Zivilisiertes umwandeln können, das andere Threads verwenden können. Eine Infrastruktur für die Kommunikation zwischen Threads ist erforderlich. Ein nützliches Muster ist das Akteurmuster, bei dem jeder Ihrer Threads ein Ziel für einen in Bearbeitung befindlichen Messaging-Mechanismus ist.

Anstatt andere Threads abzubrechen oder zu beenden (oder andere seltsame Dinge), sollten Sie versuchen, das Signal aus dem Signal-Kontext in Ihren Signal-Handler-Thread zu leiten und dann mithilfe Ihrer Kommunikationsmechanismen für das Akteurmuster semantisch nützliche Nachrichten an diese Akteure zu senden. Wer braucht die signalbezogenen Informationen.

user2173833
quelle