2015-10-30 10 views
12

Capisco che il thread che attende una variabile condizionale, rilasci atomicamente il blocco e vada in sospensione fino a quando non viene risvegliato da un segnale condizionale da un altro thread (quando viene soddisfatta una particolare condizione). Dopo essersi risvegliato, riacquista atomicamente il blocco (in qualche modo magicamente) e aggiorna come richiesto e sblocca la sezione critica.In che modo è implementato conditional_wait() a livello di kernel e hardware/assembly?

Sarebbe bello se qualcuno potesse spiegare come questa procedura conditional_wait() implementata al kernel e al livello hardware/assembly?

Come viene rilasciato e riacquisito il blocco atomicamente? Come lo garantisce il kernel?

Cosa vuol dire dormire qui? Significa un cambio di contesto in un altro processo/thread?

Durante la sospensione thread, in che modo questo thread si attiva segnalando implementato a livello di kernel e se viene fornito supporto hardware specifico per questi meccanismi?

Edit:

Sembra "futex" è il ragazzo la gestione di questa roba attesa/segnale. Per restringere la mia domanda: Come il sistema futex richiede l'attesa e la notifica delle variabili di condizione è implementata/funziona a basso livello?

+0

Su un sistema Linux la fonte, dal livello di libreria standard verso il basso per il kernel, è tutto aperto e liberamente disponibile. Ci vorrà del tempo per setacciare ma non è impossibile. –

+1

Su sistemi Linux, il collegamento tra lo spazio utente e il kernel viene solitamente eseguito con uno strumento chiamato futex. Combina aggiornamenti atomici con l'attesa organizzata del kernel. Dovresti essere in grado di trovare molta documentazione sui futex. –

+0

Dove dichiara che * riacquisire * il blocco dopo l'attesa è atomico? La manpage di certo non lo è. – EOF

risposta

11

Ad un livello elevato (e dal momento che stai chiedendo questo quesiton, il livello più alto è quello che ti serve) non è così complicato. In primo luogo, è necessario conoscere gli strati di responsabilità. Ci sono fondamentalmente 3 livelli: livello

  • Hardware - di solito qualcosa che può essere codificato in una singola istruzione ASM
  • livello del kernel - cosa che il kernel del sistema operativo fa livello
  • Application - cosa che l'applicazione fa

Generalmente, queste responsabilità non si sovrappongono - il kernel non può fare ciò che solo l'hardware può fare, l'hardware non può fare ciò che solo il kernel può fare. Tenendo presente questo, è utile ricordare che quando si tratta di bloccare, c'è ben poco hardware a riguardo. Si riduce praticamente fino a

  • aritmetica atomiche - hardware può bloccare una particolare regione di memoria (assicurarsi che nessun altro thread accedervi), eseguire l'operazione aritmetica su di esso e sbloccare la regione. Questo può funzionare solo sugli aritmetici supportati nativamente dal chip (senza radici quadrate!) E sulle dimensioni supportate nativamente dall'hardware
  • Barriere di memoria o recinti - ovvero, introdurre una barriera all'interno di un flusso di istruzioni, in modo che quando la CPU riordina le istruzioni o utilizza cache di memoria, non attraverseranno quelle recinzioni e la cache sarà fresca
  • Impostazioni condizionali (confronta e imposta) - imposta la regione di memoria sul valore A se è B e riporta lo stato di questa operazione (era impostato o meno)

Questo è praticamente tutto ciò che la CPU può fare. Come vedi, qui non ci sono futex, mutex o variabili condizionali. Questa roba è fatta dal kernel con operazioni supportate dalla CPU a sua disposizione.

Diamo un'occhiata a un livello molto alto di come il kernel potrebbe implementare la chiamata futex.In realtà, il futex è leggermente complicato, perché è una combinazione di chiamate a livello utente e chiamate a livello di kernel, se necessario. Esaminiamo il mutex 'puro', implementato esclusivamente nello spazio del kernel. Ad un livello elevato, sarà abbastanza dimostrativo.

Quando il mutex viene inizialmente creato, il kernel associa ad esso un'area di memoria. Questa regione manterrà il valore di mutex bloccato o sbloccato. Successivamente, al kernel viene chiesto di bloccare il mutex, in primo luogo istruisce la CPU a emettere una barriera di memoria. Un mutex deve servire da barriera, in modo che tutto il read/wrttten dopo che il mutex è acquisito (o rilasciato) sia visibile al resto delle CPU. Di esso utilizza l'istruzione di confronto e impostazione supportata dalla CPU per impostare il valore della regione di memoria su 1 se era impostato su 0. (ci sono mutex rientranti più complicati, ma non complichiamo l'immagine con essi). È garantito dalla CPU che anche se più di un thread tenta di farlo allo stesso tempo, solo uno avrà successo. Se l'operazione ha successo, ora teniamo premuto il mutex. Una volta chiesto al kernel di rilasciare il mutex, la regione di memoria è impostata su 0 (non è necessario farlo in modo condizionale, poiché sappiamo che teniamo premuto il mutex!) E viene emessa un'altra barriera di memoria. Il kernel aggiorna anche lo stato mutex nelle sue tabelle - vedi sotto.

Se il blocco mutex fallisce, il kernel aggiunge il thread alle sue tabelle che elencano i thread in attesa su particolari mutex da rilasciare. Quando il mutex viene rilasciato, il kernel controlla quali thread sono in attesa su questo mutex, e 'schedule' (cioè prepara per l'esecuzione) uno di questi (nel caso ce ne sia più di uno, quale sarà programmato o risveglio dipende da moltitudine di fattori, nel caso più semplice è semplicemente casuale). Il thread pianificato inizia l'esecuzione, blocca nuovamente il mutex (a questo punto può fallire di nuovo!) E il ciclo della vita continua.

spero che fa di almeno la metà-senso :)

+0

Grazie mille per la tua risposta dettagliata !! Rende le cose molto chiare sul blocco. Ho qualche altro chiarimento. Cosa vuol dire ** dormire ** dopo lo sblocco? Significa un passaggio a un altro processo/thread dal kernel?come si risveglia questo thread con ** signaling **? Cosa significa segnalare il filo dormiente? Come è implementato. – Shyam

+1

'sleep' indica che il thread invia una chiamata a una variante della funzione sleep (ad esempio,' nanosleep'), supportata dal kernel. Dopo aver visto quella chiamata, lo scheduler prende il filo che ha emesso la chiamata dall'esecuzione, e pianifica la successiva esecuzione da eseguire dopo il timeout del sonno. Nota, a meno che tu non stia parlando di un vero sistema in tempo reale, il thread non verrà eseguito ESATTAMENTE dopo il timeout. Sarà eseguito non prima del timeout! Il segnale è una voce nelle tabelle dei segnali all'interno del kernel. Una volta che il kernel lo vede, chiama la funzione di gestione dei segnali definita nella discussione. – SergeyA

Problemi correlati