POSIX-threads en signalen

Ik heb geprobeerd de fijne kneepjes van de interactie tussen POSIX-threads en POSIX-signalen te begrijpen. Ik ben in het bijzonder geïnteresseerd in:

  • Wat is de beste manier om te bepalen aan welke thread een signaal wordt afgeleverd (ervan uitgaande dat het in de eerste plaats niet dodelijk is)?
  • Wat is de beste manier om een ​​andere thread (die mogelijk bezet is) te vertellen dat het signaal is aangekomen? (Ik weet al dat het een slecht idee is om pthread-conditievariabelen van een signaalhandler te gebruiken.)
  • Hoe kan ik veilig omgaan met het doorgeven van de informatie dat een signaal is opgetreden naar andere threads? Moet dit in de signaalhandler gebeuren? (Over het algemeen wil ik de andere threads niet doden; ik heb een veel subtielere aanpak nodig.)

Voor informatie over waarom ik dit wil, onderzoek ik hoe ik het TclX-pakket naar ondersteuningsthreads, of om het op te splitsen en op zijn minst enkele nuttige onderdelen te maken, ondersteuningsthreads. Signalen zijn een van die onderdelen die van bijzonder belang zijn.


Antwoord 1, autoriteit 100%

  • Wat is de beste manier om te bepalen welke thread?
    een signaal wordt geleverd aan?

Zoals @zoli2k al aangaf, is het een goede techniek om expliciet een enkele thread te nomineren voor het afhandelen van alle signalen die u wilt verwerken (of een reeks threads met elk specifieke signaalverantwoordelijkheden).

  • Wat is de beste manier om een ​​andere thread te vertellen (die misschien wel druk is)
    dat het signaal is aangekomen?[…]
  • Hoe kan ik veilig omgaan met het doorgeven van de informatie dat er een signaal is opgetreden?
    naar andere topics? Moet dit gebeuren in de signaalhandler?

Ik zal niet zeggen “beste”, maar hier is mijn aanbeveling:

Blokkeer alle gewenste signalen in main, zodat alle threads dat signaalmasker overnemen. Vorm vervolgens de speciale signaalontvangende thread als een signaalgestuurde gebeurtenislus, waarbij nieuw binnengekomen signalen worden verzonden als een andere intra-thread-communicatie.

De eenvoudigste manier om dit te doen, is door de thread signalen in een lus te laten accepteren met behulp van sigwaitinfoof sigtimedwait. De thread converteert vervolgens de signalen op de een of andere manier, misschien door een pthread_cond_tuit te zenden, andere threads wakker te maken met meer I/O, een commando in een applicatiespecifieke thread-safe wachtrij te plaatsen, wat dan ook.

Als alternatief kan de speciale thread het mogelijk maken om signalen aan een signaalbehandelaar te leveren, waarbij de maskering voor levering alleen wordt ontmaskerd als ze klaar zijn om signalen te verwerken. (Signaallevering via handlers is echter meestal foutgevoeliger dan signaalacceptatie via de sigwait-familie.) In dit geval voert de signaalhandler van de ontvanger een eenvoudige en async-signaalveilige actie uit: instelling sig_atomic_tvlaggen, aanroepend sigaddset(&signals_i_have_seen_recently, latest_sig), write() een byte naar een niet-blokkerende self-pipe, enz. Vervolgens, terug in de gemaskeerde hoofdlus, communiceert de thread de ontvangst van het signaal naar andere threads zoals hierboven.

(GE-UPDATE@caf wijst er terecht op dat sigwait-benaderingen superieur zijn.)


Antwoord 2, autoriteit 31%

Volgens de POSIX-standaard moeten alle threads met dezelfde PID op het systeem verschijnen en met pthread_sigmask()kunt u het signaalblokkeringsmasker voor elke thread definiëren.

Omdat het is toegestaan ​​om slechts één signaalhandler per PID te definiëren, geef ik er de voorkeur aan om alle signalen in één thread te verwerken en pthread_cancel()te sturen als een actieve thread moet worden geannuleerd. Het is de geprefereerde manier tegen pthread_kill()omdat het de mogelijkheid biedt om opschoonfuncties voor de threads te definiëren.

Op sommige oudere systemen kunnen de actieve threads, vanwege het ontbreken van de juiste kernelondersteuning, een andere PID hebben dan de PID van de bovenliggende thread. Zie FAQ voor signaalverwerking met linuxThreads op Linux 2.4.


Antwoord 3, autoriteit 8%

Waar ben ik tot nu toe:

  • Signalen zijn er in verschillende hoofdklassen, waarvan sommige normaal gesproken het proces toch gewoon moeten stoppen (SIGILL) en waarvan sommige nooit iets hoeven te doen (SIGIO; gemakkelijker om toch gewoon asynchrone IO goed te doen). Die twee klassen hebben geen actie nodig.
  • Sommige signalen hoeven niet meteen te worden aangepakt; mensen als SIGWINCH kunnen in de wachtrij worden geplaatst totdat het uitkomt (net als een evenement van X11).
  • De lastige zijn degene waar je op wilt reageren door te onderbreken wat je aan het doen bent, maar zonder zo ver te gaan dat een thread wordt weggevaagd. Met name SIGINT in interactieve modus zou dingen responsief moeten laten.

Ik moet signalvs sigaction, pselect, sigwait, sigaltstack, en een hele reeks andere stukjes en beetjes POSIX (en niet-POSIX) API.


Antwoord 4, autoriteit 8%

IMHO, Unix V-signalen en posix-threads gaan niet goed samen.
Unix V is 1970. POSIX is 1980 😉

Er zijn annuleringspunten en als je signalen en pthreads in één applicatie toestaat, zul je uiteindelijk loops rond elke oproep schrijven, wat verrassend EINTR kan opleveren.

Dus wat ik deed in de (paar) gevallen waarin ik multithreaded op Linux of QNX moest programmeren, was om alle signalen voor alle (op één na) threads te maskeren.

Wanneer een Unix V-signaal arriveert, verandert het proces de stapel (dat was zoveel gelijktijdigheid in Unix V als je binnen een proces kon krijgen).

Zoals de andere berichten hier aangeven, is het nu misschien mogelijk om het systeem te vertellen welke posix-thread het slachtoffer zal zijn van die stapelwisseling.

Zodra je erin geslaagd bent om je Signal handler-thread werkend te krijgen, blijft de vraag hoe je de signaalinformatie kunt transformeren naar iets beschaafds dat andere threads kunnen gebruiken. Een infrastructuur voor communicatie tussen threads is vereist. Een handig patroon is het actorpatroon, waarbij elk van je threads een doelwit is voor een of ander in-process Messaging-mechanisme.

Dus, in plaats van andere threads te annuleren of ze te doden (of andere rare dingen), moet je proberen het Signaal van de Signal-context naar je Signal-handlerthread te rangschikken, en vervolgens je actorpatrooncommunicatiemechanismen gebruiken om semantisch bruikbare berichten naar die actoren, die de signaalgerelateerde informatie nodig hebben.

Other episodes