POSIX-Threads und -Signale

Lesezeit: 6 Minuten

Benutzeravatar von Donal Fellows
Donal Fellows

Ich habe versucht, die Feinheiten zu verstehen, wie POSIX-Threads und POSIX-Signale interagieren. Insbesondere interessiere ich mich für:

  • Was ist der beste Weg, um zu steuern, an welchen Thread ein Signal geliefert wird (vorausgesetzt, es ist nicht von vornherein fatal)?
  • Was ist der beste Weg, um einem anderen Thread (der möglicherweise beschäftigt ist) mitzuteilen, dass das Signal angekommen ist? (Ich weiß bereits, dass es keine gute Idee ist, pthread-Bedingungsvariablen von einem Signalhandler zu verwenden.)
  • Wie kann ich die Information, dass ein Signal aufgetreten ist, sicher an andere Threads weitergeben? Muss dies im Signalhandler geschehen? (Ich möchte die anderen Threads im Allgemeinen nicht töten; ich brauche einen viel subtileren Ansatz.)

Als Referenz, warum ich das möchte, recherchiere ich, wie man das konvertiert TclX Paket, um Threads zu unterstützen, oder um es aufzuteilen und zumindest einige nützliche Teile zu unterstützen, die Threads unterstützen. Signale sind einer der Teile, die von besonderem Interesse sind.

Benutzeravatar von pilcrow
Pilkrähe

  • Was ist der beste Weg, um zu steuern, an welchen Thread ein Signal geliefert wird?

Wie @zoli2k angedeutet hat, ist die explizite Nominierung eines einzelnen Threads für die Verarbeitung aller Signale, die Sie verarbeiten möchten (oder einer Reihe von Threads mit jeweils bestimmten Signalzuständigkeiten), eine gute Technik.

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

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

Sperren Sie alle gewünschten Signale ein main, sodass alle Threads diese Signalmaske erben. Gestalten Sie dann den speziellen Signalempfangs-Thread als eine signalgesteuerte Ereignisschleife, die neu angekommene Signale als versendet eine andere Intra-Thread-Kommunikation.

Der einfachste Weg, dies zu tun, besteht darin, den Thread Signale in einer Schleife mit akzeptieren zu lassen sigwaitinfo oder sigtimedwait. Der Thread wandelt die Signale dann irgendwie um, vielleicht Broadcasting pthread_cond_tandere Threads mit mehr E/A aufwecken, einen Befehl in eine anwendungsspezifische Thread-sichere Warteschlange einreihen, was auch immer.

Alternativ könnte der spezielle Thread zulassen, dass Signale an einen Signalhandler geliefert werden, wobei die Maskierung für die Lieferung nur dann erfolgt, wenn er bereit ist, Signale zu verarbeiten. (Die Signalzustellung über Handler ist tendenziell fehleranfälliger als die Signalannahme über die sigwait Familie.) In diesem Fall führt der Signalhandler des Empfängers eine einfache und asynchronsignalsichere Aktion aus: die Einstellung sig_atomic_t Fahnen, Aufruf sigaddset(&signals_i_have_seen_recently, latest_sig), write() ein Byte zu einem nicht-blockierenden Selbstpfeifeusw. Dann teilt der Thread, zurück in seiner maskierten Hauptschleife, den Empfang des Signals wie oben an andere Threads mit.

(AKTUALISIERT @caf weist zu Recht darauf hin sigwait Ansätze sind überlegen.)

  • Das ist eine viel nützlichere Antwort, zumal sie auch für die Verarbeitung nicht tödlicher Signale verwendet werden kann. Vielen Dank!

    – Donal Fellows

    23. April 2010 um 20:49 Uhr

  • Am einfachsten ist es, wenn der Signalverarbeitungs-Thread überhaupt keine Signalhandler installiert, sondern in einer Schleife weiterläuft sigwaitinfo() (oder sigtimedwait()) und leitet sie dann wie im letzten Absatz beschrieben an den Rest der Anwendung weiter.

    – Café

    20. Juli 2015 um 6:55 Uhr

  • @caf, in der Tat. Aktualisiert

    – Pilkrähe

    20. Juli 2015 um 16:37 Uhr

Benutzeravatar von zoli2k
zoli2k

Nach dem POSIX-Standard sollten alle Threads mit der gleichen PID auf dem System erscheinen und verwenden pthread_sigmask() Sie können die Signalblockierungsmaske für jeden Thread definieren.

Da pro PID nur ein Signalhandler definiert werden darf, bevorzuge ich es alle Signale in einem Thread zu behandeln und zu senden pthread_cancel() wenn ein laufender Thread abgebrochen werden muss. Es ist der bevorzugte Weg dagegen pthread_kill() da es erlaubt, Bereinigungsfunktionen für die Threads zu definieren.

Auf einigen älteren Systemen können die laufenden Threads aufgrund des Mangels an geeigneter Kernel-Unterstützung eine andere PID als die PID des übergeordneten Threads haben. Siehe FAQ zur Signalbehandlung mit linuxThreads unter Linux 2.4.

  • In dem, was Sie sagen, bedeutet “implementiert” was? Außerdem ist es nicht richtig, andere Threads immer als Reaktion auf ein Signal zu nukleieren (SIGHUP und SIGWINCH erfordern mehr Subtilität), und dennoch ist es unsicher, Bedingungsvariablen zu verwenden, um andere Threads darüber zu informieren. Schlechte Antwort.

    – Donal Fellows

    5. April 2010 um 6:09 Uhr

  • Meine Ablehnung wurde entfernt, aber es ist immer noch keine ausreichende Antwort, da ich Threads nicht einfach als Reaktion auf ein Signal beenden kann. In einigen Fällen werde ich als Reaktion Ereignisse lokal in die Warteschlange stellen, in anderen muss ich Threads sehr sorgfältig abreißen (Übrigens, ich habe bereits die meisten Maschinen, um diese Teile zu erledigen; es sind die Verbindungen zum Betriebssystem fehlende Signale).

    – Donal Fellows

    5. April 2010 um 7:53 Uhr


  • @zoli2k: Ich habe erst kürzlich versucht zu laufen make menuconfig mit frisch geklontem Git-Master-Zweig von uClibc. Dort ist eine Wahl zwischen den alten LinuxThreads und den neueren NPTL als POSIX-Thread-Implementierungen, aber die Hilfe aus dem Jahr 2012 rät immer noch davon ab, NPTL zu wählen. Daher ist es in modernen Embedded-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

    25. Mai 2012 um 4:14 Uhr

Wo bin ich bisher:

  • Signale gibt es in verschiedenen Hauptklassen, von denen einige den Prozess normalerweise sowieso beenden sollten (SIGILL) und andere nie etwas tun müssen (SIGIO; es ist sowieso einfacher, asynchrone E / A richtig zu machen). Diese beiden Klassen brauchen keine Aktion.
  • Einige Signale müssen nicht sofort behandelt werden; Dinge wie SIGWINCH können in die Warteschlange gestellt werden, bis es passend ist (genau wie ein Ereignis von X11).
  • Die kniffligen sind diejenigen, bei denen Sie darauf reagieren möchten, indem Sie Ihre Arbeit unterbrechen, ohne jedoch so weit zu gehen, dass ein Thread gelöscht wird. Insbesondere SIGINT im interaktiven Modus sollte die Dinge reaktionsfähig lassen.

Ich muss mich noch sortieren signal vs sigaction, pselect, sigwait, sigaltstackund eine ganze Reihe anderer Teile der POSIX- (und Nicht-POSIX-) API.

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

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

Also habe ich in den (wenigen) Fällen, in denen ich unter Linux oder QNX multithreaded programmieren musste, alle Signale für alle (bis auf einen) Threads ausgeblendet.

Wenn ein Unix-V-Signal ankommt, wechselt der Prozess den Stack (das war so viel Parallelität in Unix V, 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 prozessinternen Messaging-Mechanismus ist.

Anstatt also andere Threads abzubrechen oder zu beenden (oder andere seltsame Dinge), sollten Sie versuchen, das Signal aus dem Signalkontext in Ihren Signal-Handler-Thread zu leiten und dann Ihre Akteursmuster-Kommunikationsmechanismen verwenden, um semantisch nützliche Nachrichten an diese Akteure zu senden. die die signalbezogenen Informationen benötigen.

1419240cookie-checkPOSIX-Threads und -Signale

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy