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 :)
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. –
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. –
Dove dichiara che * riacquisire * il blocco dopo l'attesa è atomico? La manpage di certo non lo è. – EOF