2010-11-19 5 views
10

Sulla maggior parte delle implementazioni di thread POSIX, è necessaria un'inizializzazione nel thread appena creato prima che sia in uno stato coerente in grado di eseguire il codice dell'applicazione. Ciò può comportare lo sblocco dei blocchi nella struttura del thread, l'inizializzazione del "registro dei thread" nelle implementazioni che ne utilizzano uno, l'inizializzazione dei dati locali del thread (dati TLS a livello di compilatore o specifici del thread POSIX), ecc. Non riesco a trovare un chiaro garantire che tutta questa inizializzazione sarà terminata prima che il thread possa ricevere qualsiasi segnale; il più vicino che riesco a trovare è in 2.4.3:I segnali di garanzia POSIX non verranno consegnati a un thread parzialmente inizializzato?

La seguente tabella definisce un insieme di funzioni che devono essere sicure per il segnale asincrono. Pertanto, le applicazioni possono invocateli, senza restrizioni, funzioni di segnalazione cattura:

...

Presumibilmente, alcune di queste funzioni (almeno fork, che deve ispezionare stato globale stabilito dal pthread_atfork funzione) dipende dal fatto che il thread si trovi in ​​uno stato coerente e inizializzato.

Una cosa che mi dà fastidio è che ho letto molto del codice glibc/nptl e non riesco a trovare alcuna sincronizzazione esplicita per impedire che un segnale venga gestito dal thread appena creato prima che sia completamente inizializzato. Mi aspetterei che il thread chiamasse pthread_create per bloccare tutti i segnali prima di chiamare clone, e per il nuovo thread per sbloccarli una volta che l'inizializzazione è terminata, ma non riesco a trovare alcun codice per quell'effetto né lo vedo nell'output strace.

+3

Wow, chiamare 'fork()' da un gestore di segnale quando i gestori di 'pthread_atfork()' sono stati impostati ... Dovresti * davvero * sapere cosa stai facendo (e fidarti della tua implementazione della libreria) per quello! Soprattutto se (come avviene normalmente) il gestore di prefork afferra un gruppo di lucchetti per garantire che i dati che rappresentano siano coerenti prima della forcella: in linea di principio questi blocchi potrebbero essere mantenuti (o peggio, nel processo di acquisizione) dal thread che gestisce il segnale, il che significa che i dati sono irreparabilmente incoerenti (o il processo potrebbe deadlock!). Tutto molto divertente :-) – psmears

+0

Bene, 'fork' è elencato come una delle funzioni async-segnale-safe, ma sono d'accordo sul fatto che praticamente tutte le cose utili che una funzione' pthread_atfork'-registrata potrebbe fare non sono un segnale asincrono -sicuro. Esistono ancora alcuni usi validi, ad esempio se il tuo gestore 'pthread_atfork' semplicemente reinizializza i dati con valori fissi, distrugge i mutex controllanti e inizializza quelli nuovi (tutto ovviamente nel processo figlio). –

+0

La cosa più disturbante è cosa succede se una libreria (non nota per essere trasmessa dall'applicazione chiamante, magari caricata anche in modo dinamico indirettamente come dipendenza da un'altra libreria) imposta i gestori di 'pthread_atexit' che non sono sicuri per il segnale asincrono. L'applicazione chiamante potrebbe aspettarsi che 'fork' sia async-signal-safe (come documentato) e lo chiami da un gestore di segnale. Immagino che quello che mi viene in mente da questo esperimento mentale sia che espone un difetto nel modello di librerie "usa e gira-filetto" per la creazione di "pthread_atfork". –

risposta

1

(non credo che questa sia una vera risposta, ma è grande per un commento)

Questa è una domanda molto interessante. Ho esaminato il codice glibc per pthread_create per vedere come si comporta e, a meno che mi manchi completamente qualcosa, non sembra esserci alcun comportamento speciale per fermarlo (come bloccare tutti i segnali prima dello clone e sbloccarli nel child after some setup {dopo aver registrato il tempo di creazione del thread e il C++ cattura tutto il gestore di eccezioni è impostato, cosa che accade anche nel codice C}).

Mi aspettavo di trovare un commento che menzionasse la possibilità di questa situazione e forse anche una menzione di ciò che POSIX ha detto di fare (o una menzione che non ha detto cosa fare).

Forse dovresti sempre inserire il codice pthread_create nel codice per bloccare e ripristinare i segnali e avviare tutte le funzioni del thread con una chiamata di sblocco.

Questo potrebbe essere un over site in pthreads (o glibc o la mia comprensione del codice).

+0

Il meglio che posso raccogliere è che se i segnali non sono bloccati nel nuovo thread fino al termine dell'inizializzazione, l'implementazione deve almeno garantire che ciò non causi la rottura di nessuna delle funzioni di sicurezza del segnale asincrono.Poiché '__thread' non è ancora standard e quindi non fa parte di POSIX, non è ovviamente richiesto che le variabili TLS siano accessibili o abbiano i loro valori corretti. Forse una possibile implementazione (non ho ancora controllato se glibc lo fa) è di avvolgere tutti i gestori di segnale con un gestore che termina l'inizializzazione del thread (se necessario) prima di chiamare il gestore dell'app. –

+0

Dubito che gli glibc comprendano gestori di segnale del genere. Penso che farlo lo richiederebbe duplicare la gestione del segnale che il kernel fa e bloccare e ripristinare i segnali per ogni chiamata a 'sigaction' per evitare di introdurre una condizione di competizione. Ho appena guardato e non sembra farlo. È anche una cosa da fare, essere molto difficile, e potrebbe richiedere che il normale codice di avvio del thread sia in grado di riconoscere che è stato interrotto e non dovrebbe ripetere le cose che erano già state fatte. – nategoose

+0

Non mi sorprenderebbe che i vecchi gestori di segnale avvolgessero LinuxThreads. E 'stato un orribile hack. :-) –

-1

pthread_create è una chiamata di blocco. Non è presente alcun (nuovo) thread per inviare il segnale prima della chiamata, e è un thread per inviare il segnale a dopo la chiamata, quindi l'ID del thread viene restituito dalla chiamata.

Pertanto, mi piacerebbe concludere che il filo deve essere valido e inizializzato in quel momento ...

+0

Non seguo. La mia domanda non riguarda 'pthread_create' che invia un segnale al nuovo thread, ma su altri segnali (generati da altri thread nello stesso processo, altri processi o il kernel) che vengono consegnati al nuovo thread prima che sia inizializzato. –

+0

Questo non è vero, pthread_create è (comunemente) non una chiamata atomica. – nos

0

I POSIX pthread_create specification mandati questo dalla mia comprensione di:

Lo stato del nuovo segnale il thread deve essere inizializzato come segue:

  • La maschera del segnale deve essere ereditata dal thread di creazione.
  • Il set di segnali in sospeso per il nuovo thread deve essere vuoto.

Ma non ho abbastanza esperienza per dire che le cose stanno in questo modo in varie implementazioni.

+1

Penso che significhi strettamente che il nuovo thread non eredita o condivide segnali in sospeso dal suo genitore o creatore. – nategoose

+0

Infatti. Non vedo alcun modo per interpretare quella affermazione come dicendo che il thread non riceverà segnali prima che sia in uno stato coerente. –

+0

@R - qual è il significato di "uno stato coerente"? Qual è l'impatto reale di ottenere un segnale prima di questo stato coerente e dopo un microsecondo? Presumibilmente sarebbe ancora gestito o ignorato o qualsiasi altra cosa. Non sto discutendo tanto quanto cercando di valutare l'impatto reale. – Duck