2008-11-05 19 views
13

W tradycyjnej osadzonym programowania, damy funkcję opóźnienia tak:Czy istnieje alternatywa dla trybu uśpienia() w C?

for(i=0;i<255;i++) 
    for(j=0;j<255;j++); 

zdaniem mikroprocesora, to jest jak sen() funkcja działa?

Czy istnieje alternatywa dla funkcji uśpienia() w C?

+0

ok w moim środowisku jest komputer biurkowy Windows i kompilator turbo c, czy dla środowiska komputerowego istnieje alternatywa dla snu()? (nie dla systemów wbudowanych) –

+0

Dlaczego potrzebujesz alternatywy do snu? Tradycyjnie sen będzie realizowany w sposób, który nie wymaga wykonywania pętli, jak opisujesz. Zamiast tego wątek po prostu nie zostałby zaplanowany. –

Odpowiedz

11

Alternatywy zależą od tego, co próbujesz zrobić i od systemu operacyjnego, w którym się znajdujesz.

Jeśli tylko chcesz tracić czasu, wówczas mogą one pomóc:

W większości systemów uniksowych znajdziesz funkcję „usleep”, który jest mniej lub bardziej jak sen o większej rozdzielczości. Uważaj na to, ponieważ zwykle nie może spać przez zaledwie jedną mikrosekundę.

W niektórych systemach typu unix, wybrane wywołanie systemowe może być używane z wszystkimi deskryptorami plików ustawia zero, aby uzyskać dość dokładne podsekundowe oczekiwanie.

W systemach Windows masz tryb uśpienia, który jest prawie taki sam, ale zajmuje kilka milisekund.

W wielozadaniowym systemie operacyjnym funkcja spania może czasami mieć wartość 0 jako parametr. Zwykle powoduje to, że funkcja rezygnuje z timelice, ale należy ją ponownie zaplanować natychmiast, jeśli żadne inne zadanie nie jest gotowe do uruchomienia.

24

Rodzaj opisywanej pętli nazywa się "zajętym czekaniem". W prawdziwych systemach operacyjnych spanie nie powoduje dużego oczekiwania; mówi systemowi operacyjnemu, aby nie planował procesu, dopóki okres snu się nie skończy.

+0

ok alternatywa dla snu()? –

+0

jest miejsce dla obu. Na przykład jądro systemu Windows KeStallExecutionProcessor jest zajęte, a KeDelayExecutionThread nie jest zajęte. Istnieje sytuacja, gdy chcesz czekać bez utraty kontekstu, na przykład resetuj hw -> czekaj kilka mikro -> rób resztę hw init – Ilya

+0

Zgadzam się, że są przypadki, w których zajęte oczekiwanie jest rzeczywiście odpowiednie i zaimplementowane w jądrze. np. spin-locking. Więc przypuszczam, że moja odpowiedź powinna brzmieć: "w prawdziwym systemie operacyjnym spanie nie powoduje, że jest zbyt długo". :-) –

7

Mówisz o "programowaniu wbudowanym" w OP. Jeśli wykonujesz pracę osadzoną i potrzebujesz czegoś w rodzaju uśpienia(), często dostępne są liczniki sprzętowe/timery. To będzie różnić się w zależności od architektury, więc spójrz na arkusz danych.

Jeśli nie robisz osadzony pracę, przepraszam :)

6

Jeśli używasz na pętle, lepiej wiedzą, co do kompilacji i jak długo te instrukcje wziąć na swoim danym taktowaniu , i upewnij się, że procesor wykonuje instrukcje i nic więcej (można to zrobić w systemach wbudowanych, ale jest to trudne, ponieważ blokuje przerwania).

W przeciwnym razie nie będziesz w stanie stwierdzić, ile czasu zajmie.

Wczesne gry komputerowe miały ten problem - zostały zbudowane na komputerze o prędkości 4,7 MHz, a gdy pojawiły się szybsze komputery, nie można ich było odtworzyć.

Najlepszy sposób, w jaki "sen" może zadziałać, zależy od tego, czy procesor wie, która jest godzina w danym punkcie. Niekoniecznie faktyczny czas (7:15), ale przynajmniej względny czas (8612 sekund od pewnego momentu).

W ten sposób może zastosować deltę do bieżącego czasu i czekać w pętli, aż zostanie osiągnięty bieżący + delta.

Wszystko, co zależy od liczby cykli procesora, jest z natury niewiarygodne, ponieważ procesor może przejść do innego zadania i zawiesić pętlę.

Załóżmy, że masz 16-bitowy port I/O odwzorowywany w pamięci, który procesor zwiększa się raz na sekundę. Załóżmy też, że jest w lokalizacji pamięci 0x33 w twoim systemie wbudowanym, gdzie int są również 16 bitami. Funkcja nazywa sen staje się czymś w rodzaju:

void sleep (unsigned int delay) { 
    unsigned int target = peek(0x33) + delay; 
    while (peek(0x33) != target); 
} 

Musisz upewnić się, że peek() zwraca zawartość pamięci za każdym razem (tak optymalizacji kompilatorów nie syf się logiki), a chwilę biegnie rachunku więcej niż raz na sekundę, więc nie przegapisz celu, ale są to kwestie operacyjne, które nie mają wpływu na przedstawioną przeze mnie koncepcję.

+2

Domyślam się, że chcesz 'while (peek (0x33) erikkallen

20

Jeden wspólny mechanizm jest użycie select() że jest gwarantowana do czasu, i określić czas snu jako timeout:

// Sleep for 1.5 sec 
struct timeval tv; 
tv.tv_sec = 1; 
tv.tv_usec = 500000; 
select(0, NULL, NULL, NULL, &tv); 

select() jest zazwyczaj używany do sprawdzania zestaw deskryptorów i czekać, aż przynajmniej jeden jest gotowy do wykonania operacji we/wy. Jeśli żadna nie jest gotowa (lub, w tym przypadku, jeśli nie określono fds), upłynie limit czasu.

Zaletą funkcji select() w przypadku zajętej pętli jest to, że zużywa bardzo mało zasobów podczas snu, podczas gdy zajętą ​​pętlę monopolizuje procesor w stopniu dozwolonym na podstawie jego poziomu priorytetu.

+1

Prawdopodobnie nadmierna inżynieria, ale myślałem, że select() było przerywane przez sygnały. Co się stanie, jeśli zostanie przerwane po 1/2 sekundzie? – paxdiablo

+0

Dobre złapanie - w takim przypadku użyj funkcji pselect() do maskowania sygnałów lub tworzenia własnych procedur obsługi sygnałów uśpionych. Dzięki! –

+0

Ale jest napisane [tutaj] (http://www.gnu.org/software/libc/manual/html_node/Sleeping.html) _ (tam kliknięcie na oczekiwanie IO) _, że 'select' nie jest przerywany. Problem z wyborem jest wyjaśniony [tutaj] (http://stackoverflow.com/questions/9774986/linux-select-vs-ppoll-vs-pselect) –

1

W systemie operacyjnym unix prawdopodobnie zaplanowałbyś wywołanie signal(), a twój kod po prostu zablokowałby kod do momentu podniesienia sygnału. Sygnały są przeznaczone do tego celu i są bardzo proste i wydajne.

+0

Sygnały nie są ogólnie przeznaczone do celów opóźnień, chociaż mogą być używane za to. Nie są one mechanizmem najniższego poziomu, ponieważ są skonfigurowane do asynchronicznego przerwania. Opisana powyżej opcja select() jest prawdopodobnie bardziej wydajna. – bog

5

Zajęty czekanie jest dla amatorów nawet w systemie wbudowanym, użyj źródła czasu rzeczywistego.

6

Jest więcej informacji o tym, jak sen() działa here

Nawiasem mówiąc, zajęty oczekiwania niekoniecznie jest dla amatorów - mimo że nie spalić procesor, który może chcesz użyć w innym celu. Jeśli korzystasz ze źródła czasu, ograniczasz się do ziarnistości tego źródła. NA PRZYKŁAD. jeśli masz timer 1 ms i chcesz uzyskać 500 USS, masz problem. Jeśli twój wbudowany system poradzi sobie z faktem, że będziesz brzęczał w pętli przez 500 uSec, może to być akceptowalne. I nawet jeśli masz zegar z żądaną ziarnistością, musisz również uzyskać przerwanie tego timera we właściwym czasie ... następnie wysłać obsługę przerwań ... a następnie dostać się do kodu. Czasami najbardziej potrzebna jest pętla zajęta. Czasami.

3

Spać faktycznie interfejsów z systemem operacyjnym, gdzie procesy spania są umieszczone poza kolejki planowania. Zwykle używam:

poll(0, 0, milliseconds); 

dla systemów zgodnych z POSIX. select działa również dla okien (muszą mieć natywną API (prawdopodobnie o nazwie Sleep) dla tego).

+2

Znaleźliśmy błąd w Linuksie 2.6.11.6, który spowodował, że ankieta zawiesiła cały system w określonych sytuacjach. Przełączenie na select() rozwiązało problem; nie wiem, czy nadal istnieje w najnowszych jądrach. –

4

każdy przyzwoity kompilator C będzie, bez dodatkowej pracy, usunąć kod całkowicie i opóźnienia zniknie

+4

Pamiętam niektóre testy porównawcze sprzed wielu lat, w których określony plik wykonywalny wygenerowany przez kompilator przewyższał inne kompilatory o wiele rzędów wielkości. Okazało się, że kompilator zdał sobie sprawę, że wynik pętli nie był używany nigdzie indziej, więc zoptymalizował całą pętlę nie istniejącą :-). – paxdiablo

8

byś nie używaj opublikowanego kodu do spania w systemie wbudowanym. Przyzwoity kompilator całkowicie go usunie, a nawet jeśli twój kompilator nie usunie, jest nieoptymalny, ponieważ uruchomienie procesora w ciasnej pętli spowoduje spalenie energii, co jest problemem dla systemu wbudowanego. Nawet systemy, które nie są zasilane z baterii, dbają o zużycie energii, ponieważ mniejsze zużycie energii oznacza tańsze zasilacze i chłodzenie.

Sposób, w jaki zwykle to robisz, polega na tym, że twój CPU zaimplementuje jakieś instrukcje IDLE lub SLEEP, które spowodują tymczasowe wstrzymanie przetwarzania poleceń. Zewnętrzna linia przerwań podłączona do obwodu czasowego będzie budzić procesor z powrotem w regularnych odstępach czasu, i który wskazuje procesorowi, aby sprawdzić, czy spał wystarczająco długo, a jeśli nie, powraca do trybu uśpienia.

Dokładne dane różnią się w zależności od procesora. Jeśli pracujesz jako proces w systemie operacyjnym, wywołanie typu sleep zwykle powoduje, że program planujący zawiesza twój proces, a następnie jądro decyduje, czy zaplanować inny proces, czy przeespanie procesora. Ponadto, powyższy kod nie będzie odpowiedni dla systemów czasu rzeczywistego, które wymagają gwarancji terminowych, itp. W tych przypadkach trzeba będzie uzyskać czas w pętli, znać czas trwania przerwania tak, aby wiedzieć, czy można uśpić bez przedawnienie terminu i potencjalnie przeprogramowanie licznika czasu lub zajętości oczekiwania.

+0

Co się stanie, jeśli kończy się bk1e

+0

pseudo kod, mogę zignorować te szczegóły, założyć nieskończoną wielkość int ;-) –

2

Page 20 w "Threading in C#" autorstwa Josepha Albahari ma interesującą dyskusję na ten temat. Nie możesz spać przez mniej niż 1 ms w .Net, ale DateTime.Ticks ma granularność w odstępach 100-nanosekund (= 0,1 mikrosekundy). Aby sterować moim 5-osiowym sterownikiem CNC, wystarczy zatrzymać się na 10 mikrosekund między poleceniami krokowymi. Użyłem mikroprocesora do wykonania nieprzyjemnej pętli, ale myślę, że dobrze jest przekazać procesor do pracy, jeśli i tak masz całą wiązkę, zatrzymaj wątek, kiedy tylko możesz. Przynajmniej nie zawsze będzie to ta sama.

0

próbą ... naprawdę rozwiązać ten problem, to jest coś, co działa (nie jak powyższe próby odpowiedzi) lol

Muszę jeszcze poprawić ten kod, aby go. Niewiele dodatków jest mile widziane.

// Sleep for both Windows and Linux: 
// Too bad? No one proposed you a solution that works? 
// Since Windows has no select.h nor poll.h, an implementation 
// is necessary. 
// 
// Solutions on boards are often refered to use either select or poll, but ok, what about C (not c++)? 
// 
/// implementation of poll is destined in this attempt for windows 
/// Ideally, you add this part of code to the header of you *.c file, and it might work through... 

#ifdef WIN32 
#include <time.h> 
#include <sys/time.h> 
#include <ws2tcpip.h> 
#include <Winsock2.h> 
#include <windows.h> 
/* winsock doesn't feature poll(), so there is a version implemented 
* in terms of select() in mingw.c. The following definitions 
* are copied from linux man pages. A poll() macro is defined to 
* call the version in mingw.c. 
*/ 
#define POLLIN  0x0001 /* There is data to read */ 
#define POLLPRI  0x0002 /* There is urgent data to read */ 
#define POLLOUT  0x0004 /* Writing now will not block */ 
#define POLLERR  0x0008 /* Error condition */ 
#define POLLHUP  0x0010 /* Hung up */ 
#define POLLNVAL 0x0020 /* Invalid request: fd not open */ 
struct pollfd { 
    SOCKET fd;  /* file descriptor */ 
    short events;  /* requested events */ 
    short revents; /* returned events */ 
}; 

int mingw_poll (struct pollfd *, unsigned int, int); 

#define poll(x, y, z)  mingw_poll(x, y, z) 
#endif 





int mingw_poll(struct pollfd *fds, unsigned int nfds, int timo) 
{ 
    struct timeval timeout, *toptr; 
    fd_set ifds, ofds, efds, *ip, *op; 
    int i, rc; 

    /* Set up the file-descriptor sets in ifds, ofds and efds. */ 
    FD_ZERO(&ifds); 
    FD_ZERO(&ofds); 
    FD_ZERO(&efds); 
    for (i = 0, op = ip = 0; i < nfds; ++i) { 
    fds[i].revents = 0; 
    if(fds[i].events & (POLLIN|POLLPRI)) { 
     ip = &ifds; 
     FD_SET(fds[i].fd, ip); 
    } 
    if(fds[i].events & POLLOUT) { 
     op = &ofds; 
     FD_SET(fds[i].fd, op); 
    } 
    FD_SET(fds[i].fd, &efds); 
    } 

    /* Set up the timeval structure for the timeout parameter */ 
    if(timo < 0) { 
    toptr = 0; 
    } else { 
    toptr = &timeout; 
    timeout.tv_sec = timo/1000; 
    timeout.tv_usec = (timo - timeout.tv_sec * 1000) * 1000; 
    } 

#ifdef DEBUG_POLL 
    printf("Entering select() sec=%ld usec=%ld ip=%lx op=%lx\n", 
      (long)timeout.tv_sec, (long)timeout.tv_usec, (long)ip, (long)op); 
#endif 
    rc = select(0, ip, op, &efds, toptr); 
#ifdef DEBUG_POLL 
    printf("Exiting select rc=%d\n", rc); 
#endif 

    if(rc <= 0) 
    return rc; 

    if(rc > 0) { 
     for (i = 0; i < nfds; ++i) { 
      int fd = fds[i].fd; 
     if(fds[i].events & (POLLIN|POLLPRI) && FD_ISSET(fd, &ifds)) 
      fds[i].revents |= POLLIN; 
     if(fds[i].events & POLLOUT && FD_ISSET(fd, &ofds)) 
      fds[i].revents |= POLLOUT; 
     if(FD_ISSET(fd, &efds)) 
      /* Some error was detected ... should be some way to know. */ 
      fds[i].revents |= POLLHUP; 
#ifdef DEBUG_POLL 
     printf("%d %d %d revent = %x\n", 
       FD_ISSET(fd, &ifds), FD_ISSET(fd, &ofds), FD_ISSET(fd, &efds), 
       fds[i].revents 
     ); 
#endif 
     } 
    } 
    return rc; 
} 
+0

Myślę, że ta odpowiedź jest poza torem oryginalnego pytania. To wygląda bardziej na wieloplatformowe rozwiązanie do wyboru() i wymaga sondowania. Prawdziwy sen systemu operacyjnego nie powoduje odpytywania procesu. –

1
#include <Windows.h> 

static NTSTATUS(__stdcall *NtDelayExecution)(BOOL Alertable, PLARGE_INTEGER DelayInterval) = (NTSTATUS(__stdcall*)(BOOL, PLARGE_INTEGER)) GetProcAddress(GetModuleHandle("ntdll.dll"), "NtDelayExecution"); 

static NTSTATUS(__stdcall *ZwSetTimerResolution)(IN ULONG RequestedResolution, IN BOOLEAN Set, OUT PULONG ActualResolution) = (NTSTATUS(__stdcall*)(ULONG, BOOLEAN, PULONG)) GetProcAddress(GetModuleHandle("ntdll.dll"), "ZwSetTimerResolution"); 




static void SleepShort(float milliseconds) { 
    static bool once = true; 
    if (once) { 
     ULONG actualResolution; 
     ZwSetTimerResolution(1, true, &actualResolution); 
     once = false; 
    } 

    LARGE_INTEGER interval; 
    interval.QuadPart = -1 * (int)(milliseconds * 10000.0f); 
    NtDelayExecution(false, &interval); 
} 

Tak wykorzystuje pewne nieudokumentowane funkcje jądra, ale szczerze mówiąc, to o wiele szybciej niż większość propozycji i prac bardzo dobrze, (zauważ, że będzie to ograniczone do wielkości procesora, na przykład moje może spać tylko 500 nanosekund (0,5)).

1

dostępne w linuxie usleep (int mic roseconds) nanosleep (...) więcej precyzji zobacz strony man dla wywoływania argumentów

Powiązane problemy