2010-04-04 18 views
72

Ho cercato di capire la complessità di come interagiscono i fili POSIX e i segnali POSIX. In particolare, sono interessato a:Segnali e segnali POSIX

  • Qual è il modo migliore per controllare a quale thread viene consegnato un segnale (presupponendo che non sia fatale in primo luogo)?
  • Qual è il modo migliore per dire a un altro thread (che potrebbe essere effettivamente occupato) che il segnale è arrivato? (So ​​già che si tratta di una cattiva idea di utilizzare le variabili di condizione pthread da un gestore di segnale.)
  • Come posso tranquillamente gestire il passaggio delle informazioni che un segnale è verificato ad altri thread? Questo deve succedere nel gestore del segnale? (Non in generale vogliono uccidere gli altri thread;. Ho bisogno di un approccio molto più sottile)

Per avere un riferimento sul perché voglio questo, sto ricercando come convertire il pacchetto TclX per supportare le discussioni, o per dividerlo e almeno fare alcune discussioni utili per il supporto delle parti. I segnali sono una di quelle parti che è di particolare interesse.

risposta

42
  • Qual è il modo migliore per controllare quale thread un segnale viene consegnato?

Come @ zoli2k indicato, nominando esplicitamente un singolo thread per gestire tutti i segnali che si desidera gestito (o un insieme di fili ciascuno con responsabilità di segnali specifici), è una buona tecnica.

  • Qual è il modo migliore per raccontare un altro thread (che potrebbe in realtà essere occupato) che il segnale è arrivato? [...]
  • Come posso tranquillamente gestire il passaggio delle informazioni che un segnale si è verificato in altre discussioni? Questo deve succedere nel gestore del segnale?

non dirò "migliore", ma qui è il mio consiglio:

Blocca tutti i segnali desiderati in main, in modo che tutte le discussioni sono ereditare quella maschera segnale. Quindi, adattare lo speciale thread di ricezione del segnale come un loop di eventi pilotato dal segnale, inviando i segnali appena arrivati ​​come ad un'altra comunicazione intra-thread.

Il modo più semplice per eseguire questa operazione è far sì che il thread accetti i segnali in un loop utilizzando sigwaitinfo or sigtimedwait. Il thread quindi converte i segnali in qualche modo, forse trasmettendo uno pthread_cond_t, svegliando altri thread con più I/O, accodando un comando in una coda thread-safe specifica dell'applicazione, qualunque sia.

In alternativa, la filettatura speciale potrebbe consentire la consegna di segnali a un gestore di segnale, smascheratura per la consegna solo quando è pronto a gestire i segnali. (Tuttavia, l'invio del segnale tramite handler tende ad essere più incline all'errore rispetto all'accettazione del segnale tramite la famiglia sigwait). In questo caso, il gestore del segnale del ricevitore esegue alcune azioni semplici e asincrone: impostando i flag sig_atomic_t, chiamando sigaddset(&signals_i_have_seen_recently, latest_sig), write() un byte su un blocco non bloccante self-pipe, ecc. Quindi, tornando nel suo ciclo principale mascherato, il thread comunica la ricezione del segnale ad altri thread come sopra.

(AGGIORNAMENTO @caf giustamente osserva che sigwait approcci sono superiori.)

+0

Questa è una risposta molto più utile, soprattutto perché può essere utilizzata anche per gestire la gestione del segnale non fatale. Grazie! –

+1

È più semplice se il thread di gestione del segnale non installa affatto i gestori di segnale - invece, scorre su 'sigwaitinfo()' (o 'sigtimedwait()'), quindi li invia al resto dell'applicazione come descritto nell'ultimo paragrafo. – caf

+0

@ CAF, davvero così. aggiornato – pilcrow

13

Secondo lo standard POSIX, tutti i thread devono apparire con lo stesso PID sul sistema e utilizzando pthread_sigmask() è possibile definire la maschera di blocco del segnale per ogni thread.

Poiché è consentito definire un solo gestore di segnale per PID, preferisco gestire tutti i segnali in un thread e inviare pthread_cancel() se è necessario annullare un thread in esecuzione. È il metodo preferito contro pthread_kill() poiché consente di definire le funzioni di pulizia per i thread.

Su alcuni sistemi meno recenti, a causa della mancanza di un adeguato supporto per il kernel, i thread in esecuzione possono avere PID diverso dal PID del thread genitore. Vedi FAQ per la gestione dei segnali con linuxThreads on Linux 2.4.

+0

In quello che dici "implementato" significa che cosa? Inoltre, non è corretto eseguire sempre il nuke di altri thread in risposta a un segnale (SIGHUP e SIGWINCH richiedono una maggiore sottigliezza) e tuttavia non è sicuro utilizzare le variabili di condizione per far conoscere altri thread. Povera risposta –

+0

@Donal Fellows> Post edited. Spero che ora sia più utile. – zoli2k

+1

Rimosso il mio voto negativo, ma non è ancora una risposta sufficiente perché non posso semplicemente uccidere thread in risposta a un segnale. In alcuni casi ho intenzione di mettere in coda gli eventi localmente in risposta, in altri devo strappare i thread molto attentamente (a proposito, ho la maggior parte dei macchinari per fare già quelle parti: sono le connessioni al sistema operativo segnali che mancano). –

3

dove sono a finora:

  • I segnali sono disponibili in diverse classi principali, alcuni dei quali dovrebbero in genere solo uccidere il processo in ogni caso (SIGILL) e alcuni dei quali non hanno bisogno di qualsiasi cosa fare (SIGIO; più facile fare solo IO asincrono giusto comunque). Queste due classi non hanno bisogno di azione.
  • Alcuni segnali non devono essere gestiti immediatamente; SIGWINCH può essere accodato fino a quando non è conveniente (proprio come un evento di X11).
  • I più difficili sono quelli in cui si desidera rispondere a loro interrompendo quello che si sta facendo, ma senza andare fino al punto di cancellare un thread. In particolare, SIGINT in modalità interattiva dovrebbe lasciare le cose reattive.

Ho ancora per ordinare attraverso signal vs sigaction, pselect, sigwait, sigaltstack, e tutta una serie di altri pezzi di POSIX (e non-POSIX) API.

4

IMHO, segnali Unix V e discussioni posix non si mescolano bene. Unix V è 1970. POSIX è 1980;)

ci sono punti di cancellazione e se si consente segnali e pthreads in una sola applicazione, voi alla fine finiscono per scrivere loop intorno ad ogni chiamata, che può sorprendentemente tornare EINTR.

Quindi quello che ho fatto nei (pochi) casi in cui ho dovuto programmare il multithreading su Linux o QNX era, per mascherare tutti i segnali per tutti (ma uno) i thread.

Quando arriva un segnale Unix V, il processo Commuta lo stack (era tanto la concorrenza in Unix V che si poteva ottenere all'interno di un processo).

Come gli altri post qui suggeriscono, potrebbe essere ora possibile, per dire al sistema, quale thread di posix deve essere vittima di quella commutazione di stack.

Una volta, è riuscito a far funzionare il thread del gestore di segnale, la domanda rimane, come trasformare le informazioni del segnale in qualcosa di civilizzato, altri thread possono utilizzare. È necessaria un'infrastruttura per le comunicazioni tra thread. Uno schema, utile è il pattern attore, in cui ciascuno dei thread è un obiettivo per un meccanismo di messaggistica in-process.

Quindi, invece di cancellare altri thread o ucciderli (o altre cose strane), dovresti provare a eseguire il marshalling del segnale dal contesto Signal al thread del gestore di segnale, quindi utilizzare i meccanismi di comunicazione del pattern degli attori per inviare messaggi semanticamente utili a quegli attori, che hanno bisogno delle informazioni relative al segnale.