2015-06-21 28 views
5

Zastanawiam się, w jaki sposób wewnętrznie wdrożony jest tryb spania/nanozania? Rozważmy następujący kod:Czy praca w trybie uśpienia/pracy w trybie "spania" przebiega według schematu oczekiwania zajętości?

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

by CPU robić stałą przełączania kontekstu, aby sprawdzić, czy sen o 1 sek jest wykonywana (czyli wewnętrzną czekać zajęty).

Wątpię, czy to działa w ten sposób, za dużo nieefektywności. Ale jak to działa?

To samo pytanie dotyczy nanosyleep.

Uwaga: Jeśli jest to specyficzne wdrożenie/OS, to w jaki sposób mogę ewentualnie wdrożyć bardziej wydajny schemat, który nie prowadzi do stałego przełączania kontekstu?

+1

Co jest nie tak z pytaniem? – Kam

+0

_ "Co jest nie tak z pytaniem?" _ Zbyt szeroko. Specyficzne dla systemu operacyjnego, specyficzne dla wdrożenia. –

+0

POSIX nie określa sposobu jego implementacji. – StenSoft

Odpowiedz

2

Dokładna implementacja nie jest tutaj gwarantowana, ale możesz spodziewać się pewnych właściwości.

Zazwyczaj sleep (3) jest dość niedokładna, a stany Linuksa "człowiek śpi 3" można nawet zaimplementować przy użyciu SIGALM (sygnałów). Więc zdecydowanie nie chodzi o wydajność. Z całą pewnością nie chodzi o blokady spinów, więc nie mogą one być intensywne dla procesora.

nanosleep to zupełnie inne zwierzę, które można zaimplementować nawet za pomocą spinlocków. Co jest ważniejsze, przynajmniej w Linuksie nanosleep człowiek znajduje się w sekcji 2, która oznacza, że ​​jest to wywołanie systemowe, więc przynajmniej powinien zawierać przełącznik do trybu jądra. Czy naprawdę potrzebujesz jego wysokiej rozdzielczości?

UPDATE

Jak widzę komentarz zrobić polecić select() wykorzystania jako man select 3 stanach:

#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); 
    } 

Jest sprawdzonych mechaników, jeśli chcesz spać w wątku jakiegoś zdarzenia i to zdarzenie może być połączone z deskryptorem pliku.

+0

Nie szukam wysokiej rozdzielczości, jestem bardziej zmartwiony o wydajności innych moich wątków w ciągłym przełączaniu kontekstu, ponieważ mam wątek, który śpi 99,9% czasu (ze snem (30)) – Kam

+0

30 sekund to wieczność, można bezpiecznie wyrzucić kwanty procesora wątku z snu nawet kilka milisekund bez zauważenia przez system w ogóle. –

+2

Uwaga: to pytanie jest obecnie oznaczane jako [tag: posix], a nie [tag: linux]. W POSIX 'nanosleep' nie może być zaimplementowany z blokadą spinów. Wątek musi zostać zawieszony. Czy to w Linuksie jest inne pytanie, ponieważ Linux nie jest zgodny z POSIX. –

0

"Zastanawiam się, w jaki sposób snu/nanozłożenia są wdrażane wewnętrznie?"

Nie jest realizacja jednego dla niego, ale każdy OS i zgodny z POSIX realizacja sleep() i nanosleep() są wolne, w jaki sposób jesteśmy w rzeczywistości realizacji tej funkcji.

Zapytanie o to, jak to jest właściwie zrobione, jest całkiem bezużyteczne, bez większego kontekstu konkretnej implementacji biblioteki OS/POSIX.

1

Specyfikacja Posix sleep i nanosleep powiedzieć (kopalnia nacisk)

Funkcja snu() powoduje gwint dzwonienia zawieszony wykonania aż zostanie osiągnięta liczba sekund, w czasie rzeczywistym określonych przez argument minęło sekund lub sygnał jest dostarczany do wątku wywołującego, a jego działanie polega na wywołaniu funkcji przechwytywania sygnału lub zakończeniu procesu. Czas zawieszenia może być dłuższy niż wymagany ze względu na planowanie innej aktywności przez system.

(źródłowy. http://pubs.opengroup.org/onlinepubs/9699919799/functions/sleep.html)

i

nanosleep() musi powodować obecny gwintu była zawieszony wykonania aż zostanie przedziału czasu określonego przez Argument rqtp upłynął lub sygnał został dostarczony do wątku wywołującego, a jego działanie polega na wywołaniu funkcji przechwytującej sygnał lub zakończeniu procesu. Czas zawieszenia może być dłuższy niż wymagany, ponieważ wartość argumentu jest zaokrąglana w górę do całkowitej wielokrotności rozdzielczości snu lub z powodu planowania innej aktywności przez system. Ale, z wyjątkiem przypadku przerwania przez sygnał, czas zawieszenia nie może być krótszy niż czas określony przez rqtp, mierzony przez zegar systemowy CLOCK_REALTIME.

(Źródło:. http://pubs.opengroup.org/onlinepubs/9699919799/functions/nanosleep.html)

Czytałem, że powiedzieć, że system POSIX nie mogą korzystać z ruchliwą pętlę dla sleep lub nanosleep. Wątek wywołujący musi zostać zawieszony przed wykonaniem.

3

Typowym sposobem implementacji sleep() i nanosleep() jest przekonwertowanie argumentu na dowolną skalę używaną przez program planujący systemu operacyjnego (podczas zaokrąglania w górę) i dodanie do niej bieżącego czasu w celu utworzenia "absolutnego czasu budzenia"; następnie powiedz programowi planującemu, aby nie podawał czasu procesora wątku, dopóki nie zostanie osiągnięty "absolutny czas budzenia". Nie wymaga to intensywnego oczekiwania.

Należy pamiętać, że niezależnie od skali, której używa program planujący systemu operacyjnego, zazwyczaj zależy od tego, jaki sprzęt jest dostępny i/lub używany do przechowywania danych. Może być mniejsza niż nanosekunda (np. Lokalny APIC w 80x86 używany w "trybie terminowym TSC") lub w zasięgu 100 ms.

Należy również pamiętać, że system operacyjny gwarantuje, że opóźnienie nie będzie mniejsze niż wymagane; ale zwykle nie ma gwarancji, że nie będzie dłuższy i w niektórych przypadkach (np. wątek o niskim priorytecie w mocno obciążonym systemie) opóźnienie może być o wiele większe niż wymagane. Na przykład, jeśli poprosisz o sen przez 123 nanosekundy, możesz spać przez 2 ms, zanim program planujący zdecyduje, że może dać ci czas procesora, a potem może to być kolejne 500 ms zanim planista faktycznie da ci czas procesora (np. wątki używają procesora).

Niektóre systemy operacyjne mogą próbować zmniejszyć problem "spać o wiele dłużej niż zażądano", a niektóre systemy operacyjne (np. Zaprojektowane do pracy w czasie rzeczywistym) mogą stanowić pewnego rodzaju gwarancję (z ograniczeniami - np. Z zastrzeżeniem priorytetu wątku) dla minimalny czas między wygaśnięciem opóźnienia a ponownym pobraniem procesora. Aby to zrobić, system operacyjny/jądro przekonwertowałby argument na dowolną skalę używaną przez program planujący systemu operacyjnego (podczas zaokrąglania w dół i nie zaokrąglając w górę) i może odjąć niewielką ilość "na wszelki wypadek"; tak aby program szeregujący budził wątek w górę tuż przed upływem żądanego opóźnienia (a nie po); a następnie, gdy wątek otrzymuje czas procesora (po koszcie przejścia kontekstu do wątku i ewentualnie po wstępnym pobraniu różnych linii pamięci podręcznej, z których wątek jest gwarantowany) jądro będzie zajęte przez chwilę, aż upłynie czas opóźnienia. Dzięki temu jądro może przekazać kontrolę z powrotem do wątku bardzo blisko opóźnienia wygaśnięcia.

Na przykład, jeśli poprosisz o sen przez 123 nanosekundy, program planujący może nie dać czasu procesora przez 100 nanosekund, to może poświęcić 10 nanosekund na przełączenie się do wątku, a następnie może być zajęty oczekiwaniem na pozostałe 13 nanosekund. Nawet w tym przypadku (w przypadku gdy oczekiwanie jest zakończone) zwykle nie będzie czekał na pełny czas opóźnienia.Jeśli jednak opóźnienie jest bardzo krótkie, jądro wykona tylko ostatnie, zajęte, oczekiwanie.

Wreszcie istnieje specjalny przypadek, o którym warto wspomnieć. W systemach POSIX sleep(0); jest zazwyczaj nadużywany jako yield(). Nie jestem zbyt pewny, jak uzasadniona jest ta praktyka - niemożliwe jest, aby program planujący obsługiwał coś w rodzaju yield(), chyba że planista jest skłonny tracić czas procesora, wykonując nieistotną pracę, podczas gdy ważniejsza praca czeka.