Il modo tipico di implementare sleep()
e nanosleep()
consiste nel convertire l'argomento in qualsiasi scala utilizzata dallo scheduler del sistema operativo (durante l'arrotondamento) e aggiungere l'ora corrente ad esso per formare un "tempo di attivazione assoluto"; quindi comunicare allo scheduler di non assegnare il tempo CPU del thread fino a quando non è stato raggiunto il "tempo di attivazione assoluto". Nessuna attesa impegnativa è coinvolta.
Nota che qualsiasi scala utilizzata dal programma di pianificazione del SO dipende in genere da quale hardware è disponibile e/o utilizzato per il mantenimento del tempo. Può essere più piccolo di un nanosecondo (ad esempio, l'APIC locale su 80x86 viene utilizzato in "modalità TSC deadline") o grande come 100 ms.
Si noti inoltre che il sistema operativo garantisce che il ritardo non sarà inferiore a quello richiesto; ma in genere non è garantito che non sarà più lungo e in alcuni casi (ad esempio thread a bassa priorità su un sistema pesantemente caricato) il ritardo può essere molto più grande di quanto richiesto. Ad esempio, se chiedi di dormire per 123 nanosecondi, potresti dormire per 2 ms prima che lo schedulatore decida di darti il tempo della CPU, e quindi potrebbero essere altri 500 ms prima che lo scheduler ti dia effettivamente il tempo della CPU (ad esempio perché altri i thread stanno usando la CPU).
Alcuni sistemi operativi possono tentare di ridurre questo problema "dormito molto più a lungo del richiesto" e alcuni sistemi operativi (ad esempio progettati per tempo reale) possono fornire una sorta di garanzia (con restrizioni, ad esempio soggetta alla priorità del thread) per tempo minimo tra la scadenza del ritardo e il recupero della CPU. Per fare ciò, il sistema operativo/kernel converte l'argomento in qualsiasi scala utilizzata dallo scheduler del sistema operativo (mentre arrotonda e non arrotonda) e può sottrarre una piccola quantità "nel caso in cui"; in modo che lo scheduler riattivi il thread appena prima della scadenza del ritardo richiesto (e non dopo); e poi quando il thread viene dato tempo CPU (dopo che il costo del contesto passa al thread, e possibilmente dopo aver pre-scaricato varie linee di cache che il thread è garantito da usare) il kernel dovrebbe attendere brevemente fino a quando il ritardo è effettivamente scaduto. Ciò consente al kernel di passare il controllo al thread estremamente vicino alla scadenza del ritardo. Ad esempio, se chiedi di dormire per 123 nanosecondi, allora lo scheduler potrebbe non darti un tempo di CPU di 100 nanosecondi, quindi potrebbe spendere 10 nanosecondi per passare al thread, quindi potrebbe attendere i rimanenti 13 nanosecondi. Anche in questo caso (dove l'attesa è occupata) normalmente non si attiverà per tutta la durata del ritardo.Tuttavia, se il ritardo è estremamente breve, il kernel farebbe solo l'ultimo occupato in attesa.
Infine, v'è un caso speciale che può essere degni di nota. Sui sistemi POSIX sleep(0);
viene in genere abusato come yield()
. Non sono sicuro di quanto sia legittima questa pratica: è impossibile per uno scheduler supportare qualcosa come yield()
a meno che lo scheduler non sia disposto a sprecare tempo nella CPU facendo lavori poco importanti mentre attende un lavoro più importante.
Cosa c'è di sbagliato nella domanda? – Kam
_ "Cosa c'è di sbagliato nella domanda?" _ Troppo ampio. Sistema operativo specifico, implementazione specifica. –
POSIX non specifica come dovrebbe essere implementato – StenSoft