2015-06-21 18 views
5

Mi chiedo come viene implementato il sonno/nanosleep internamente? Considerate questo codice:Il sonno/nanosleep funziona utilizzando uno schema di attesa intenso?

{ // on a thread other than main() thread 
    while(1) 
    { 
    //do something 
    sleep(1); 
    } 
} 

sarebbe la CPU essere facendo costante cambio di contesto per verificare se il sonno di 1 sec è fatto (vale a dire un'attesa occupato interna).

Dubito che funzioni in questo modo, troppa inefficienza. Ma come funziona?

La stessa domanda si applica a nanosleep.

Nota: se si tratta di un'implementazione/sistema operativo specifico, come posso implementare uno schema più efficiente che non porta a un cambio di contesto costante?

+1

Cosa c'è di sbagliato nella domanda? – Kam

+0

_ "Cosa c'è di sbagliato nella domanda?" _ Troppo ampio. Sistema operativo specifico, implementazione specifica. –

+0

POSIX non specifica come dovrebbe essere implementato – StenSoft

risposta

2

L'implementazione esatta non è garantita qui ma ci si possono aspettare alcune proprietà.

In genere sleep (3) è piuttosto impreciso e come gli stati di Linux 'uomo sonno 3' potrebbero essere implementati anche utilizzando SIGALM (segnali). Quindi non si tratta sicuramente di prestazioni. Non si tratta sicuramente di spin lock, quindi non può essere un utilizzo intensivo della CPU.

nanosleep è un animale molto diverso che potrebbe essere implementato anche utilizzando gli spinlock. Il che è più importante, almeno in Linux nanosleep man è nella sezione 2 che indica che è una chiamata di sistema quindi almeno dovrebbe includere passare alla modalità kernel. Hai davvero bisogno della sua alta risoluzione?

UPDATE

Per come la vedo il tuo commento mi faccio consigliare select() uso come man select 3 stati:

#include <stdio.h> 
    #include <stdlib.h> 
    #include <sys/time.h> 
    #include <sys/types.h> 
    #include <unistd.h> 

    int 
    main(void) 
    { 
     fd_set rfds; 
     struct timeval tv; 
     int retval; 

     /* Watch stdin (fd 0) to see when it has input. */ 
     FD_ZERO(&rfds); 
     FD_SET(0, &rfds); 

     /* Wait up to five seconds. */ 
     tv.tv_sec = 5; 
     tv.tv_usec = 0; 

     retval = select(1, &rfds, NULL, NULL, &tv); 
     /* Don't rely on the value of tv now! */ 

     if (retval == -1) 
      perror("select()"); 
     else if (retval) 
      printf("Data is available now.\n"); 
      /* FD_ISSET(0, &rfds) will be true. */ 
     else 
      printf("No data within five seconds.\n"); 

     exit(EXIT_SUCCESS); 
    } 

E 'dimostrato meccanica se avete bisogno di dormire in discussione per qualche evento e questo evento potrebbe essere collegato al descrittore di file.

+0

io non sono alla ricerca di alta risoluzione, io sono più preoccupato circa le prestazioni dei miei altri thread in commutazione di contesto costante perché ho un thread che sta dormendo il 99,9% delle volte (con sleep (30)) – Kam

+0

30 secondi è un'eternità, puoi tranquillamente buttare via il thread CPU quanti con posti letto anche un paio di millisecondi senza che il sistema se ne accorga. –

+2

Nota: questa domanda è attualmente contrassegnata come [tag: posix], non [tag: linux]. In POSIX, 'nanosleep' non può essere implementato con spin lock. Il thread deve essere sospeso. Se questo è il caso in Linux è una domanda diversa perché Linux non è compatibile con POSIX. –

0

"Mi chiedo come viene implementato il sonno/nanosleep internamente?"

non C'è l'attuazione uno per questo, ma ogni sistema operativo e conforme a POSIX realizzazione di sleep() e nanosleep() sono liberi nel modo in cui sono effettivamente attuare questa funzione.

Quindi chiedersi come è effettivamente fatto è piuttosto inutile, senza più contesto di una particolare implementazione della libreria OS/POSIX.

1

La specifica POSIX di sleep e nanosleep dire (sottolineatura mia)

La funzione sleep() è causa il thread chiamante sia sospeso dall'esecuzione fino a quando il numero di secondi in tempo reale dall'argomento secondi sono trascorsi o viene inviato un segnale al thread chiamante e la sua azione è di richiamare una funzione di cattura del segnale o di terminare il processo. Il tempo di sospensione può essere più lungo di quanto richiesto a causa della pianificazione di altre attività da parte del sistema.

(fonte:. http://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html)

e

Il nanosleep() funzione deve causare il thread corrente sia sospeso dall'esecuzione fino a quando l'intervallo di tempo specificato dal l'argomento rqtp è scaduto o viene inviato un segnale al thread chiamante e la sua azione è di richiamare una funzione di cattura del segnale o di terminare il processo. Il tempo di sospensione può essere più lungo di quanto richiesto poiché il valore dell'argomento viene arrotondato a un multiplo intero della risoluzione del sonno oa causa della pianificazione di altre attività da parte del sistema. Tuttavia, salvo il caso in cui venga interrotto da un segnale, il tempo di sospensione non deve essere inferiore al tempo specificato da rqtp, come misurato dall'orologio di sistema CLOCK_REALTIME.

(Fonte:. http://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html)

ho letto che a dire che un sistema POSIX-compliant non può utilizzare un ciclo occupato per sleep o nanosleep. Il thread chiamante deve essere sospeso dall'esecuzione.

3

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.

Problemi correlati