In attesa di read()
o write()
to/from a file descriptor return, il processo verrà messo in uno speciale tipo di sospensione, noto come "D" o "Disk Sleep". Questo è speciale, perché il processo non può essere interrotto o interrotto mentre si trova in tale stato. Anche un processo in attesa di un ritorno da ioctl() verrebbe messo a dormire in questo modo.
Un'eccezione a questo è quando un file (come un terminale o un altro dispositivo a caratteri) viene aperto in O_NONBLOCK
mode, passato quando si presume che un dispositivo (come un modem) avrà bisogno di tempo per inizializzarsi. Tuttavia, hai indicato i dispositivi di blocco nella tua domanda. Inoltre, non ho mai provato un ioctl()
che rischia di bloccarsi su un fd aperto in modalità non bloccante (almeno non consapevolmente).
Il modo in cui viene scelto un altro processo dipende interamente dallo scheduler che stai utilizzando, nonché da ciò che altri processi potrebbero aver fatto per modificare i loro pesi all'interno di quello scheduler.
È noto che alcuni programmi in spazio utente in determinate circostanze rimangono in questo stato per sempre, fino al riavvio. Questi sono in genere raggruppati con altri "zombi", ma il termine non sarebbe corretto in quanto non sono tecnicamente defunti.
Un processo che esegue l'I/O verrà messo in stato D (sospensione non interrompibile) , che libera la CPU finché non si verifica un interrupt hardware che indica alla CPU di tornare all'esecuzione del programma. Vedi man ps
per gli altri stati del processo.
A seconda del tuo kernel, c'è uno pianificatore di processi , che tiene traccia di una coda di esecuzione dei processi pronti per l'esecuzione. Insieme a un algoritmo di pianificazione, dice al kernel quale processo assegnare a quale CPU. Ci sono processi del kernel e processi utente da considerare. A ogni processo viene assegnata una porzione di tempo, che è una parte del tempo della CPU che può utilizzare. Una volta che il processo utilizza tutto il suo intervallo di tempo, viene contrassegnato come scaduto e gli viene assegnata una priorità inferiore nell'algoritmo di pianificazione.
Nel kernel 2.6 , esiste un schedulatore di complessità temporale O(1) , quindi non importa quanti processi hai in esecuzione, assegnerà le CPU in tempo costante. Tuttavia, è più complicato, dal momento che 2.6 ha introdotto la prelazione e il bilanciamento del carico della CPU non è un algoritmo facile. In ogni caso, è efficiente e le CPU non rimarranno inattive mentre aspetti l'I/O.
Quando un processo deve recuperare i dati da un disco, interrompe effettivamente l'esecuzione sulla CPU per consentire l'esecuzione di altri processi poiché l'operazione potrebbe richiedere molto tempo per il completamento:almeno 5 ms di tempo di ricerca per un disco sono comuni e 5 ms sono 10 milioni Cicli della CPU, un'eternità dal punto di vista del programma!
Dal punto di vista del programmatore (detto anche "nello spazio utente"), si tratta di una chiamata di sistema bloccante . Se chiami write(2)
(che è un sottile wrapper libc attorno alla chiamata di sistema con lo stesso nome), il tuo processo non si ferma esattamente a quel confine; continua, nel kernel, a eseguire il codice della chiamata di sistema. Il più delle volte arriva fino a uno specifico driver del controller del disco (nome file → filesystem/VFS → dispositivo a blocchi → driver del dispositivo), dove un comando per recuperare un blocco su disco viene inviato all'hardware appropriato, che è molto funzionamento veloce per la maggior parte del tempo.
ALLORA il processo viene messo in stato di sospensione (nello spazio del kernel, il blocco è chiamato dormiente – niente è mai 'bloccato' dal punto di vista del kernel). Verrà risvegliato una volta che l'hardware avrà finalmente recuperato i dati corretti, quindi il processo verrà contrassegnato come eseguibile e sarà programmato. Alla fine, lo scheduler eseguirà il processo.
Infine, nello spazio utente, la chiamata di sistema di blocco ritorna con stato e dati corretti e il flusso del programma continua.
È possibile richiamare la maggior parte delle chiamate di sistema I/O in modalità non bloccante (vedi O_NONBLOCK
in open(2)
e fcntl(2)
). In questo caso, le chiamate di sistema ritornano immediatamente e segnalano solo l'invio dell'operazione del disco. Il programmatore dovrà verificare esplicitamente in un secondo momento se l'operazione è stata completata, con successo o meno, e recuperarne il risultato (ad esempio, con select(2)
). Questa è chiamata programmazione asincrona o basata su eventi.
La maggior parte delle risposte qui menzionano lo stato D (che si chiama TASK_UNINTERRUPTIBLE
nei nomi di stato di Linux) non sono corretti. La D stato è una modalità di sospensione speciale che viene attivata solo in un percorso di codice dello spazio del kernel, quando quel percorso di codice non può essere interrotto (perché sarebbe troppo complesso da programmare), con l'aspettativa che si bloccasse solo per un tempo molto breve. Credo che la maggior parte degli "stati D" siano effettivamente invisibili; hanno vita molto breve e non possono essere osservati da strumenti di campionamento come 'top'.
Puoi incontrare processi inarrestabili nello stato D in alcune situazioni. NFS è famoso per questo e l'ho incontrato molte volte. Penso che ci sia uno scontro semantico tra alcuni percorsi di codice VFS, che presuppongono di raggiungere sempre i dischi locali e il rilevamento rapido degli errori (su SATA, un timeout di errore sarebbe di circa 100 ms), e NFS, che in realtà recupera i dati dalla rete che è più resiliente e ha un ripristino lento (è comune un timeout TCP di 300 secondi). Leggi questo articolo per la fantastica soluzione introdotta in Linux 2.6.25 con TASK_KILLABLE
stato. Prima di questa era c'era un hack in cui potevi effettivamente inviare segnali ai client di elaborazione NFS inviando un SIGKILL al thread del kernel rpciod
, ma dimentica quel brutto trucco...