Domanda

Nella tradizionale programmazione integrata, daremo una funzione di ritardo in questo modo:

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

Dal punto di vista del microprocessore, è così che funziona la funzione sleep ()?

Esiste un'alternativa per la funzione sleep () in C?

È stato utile?

Soluzione

Le alternative dipendono da cosa stai cercando di fare e da quale SO sei su.

Se vuoi solo perdere tempo, questi potrebbero aiutare:

Sulla maggior parte dei sistemi di tipo unix troverai una funzione 'usleep', che è più o meno come dormire con una risoluzione maggiore. Fai attenzione a quello perché di solito non riesce a dormire per un solo microsecondo.

Su alcuni sistemi di tipo unix, la chiamata di sistema select può essere utilizzata con tutti i set di descrittori di file zero per ottenere un'attesa dei secondi inferiore abbastanza accurata.

Sui sistemi Windows, hai Sleep, che è praticamente lo stesso, ma richiede un numero di millisecondi.

In un sistema operativo multi-tasking, a volte una funzione sleep può essere assegnata a 0 come parametro. Questo in genere fa sì che la funzione rinunci al suo timeslice, ma venga riprogrammata immediatamente se nessun'altra attività è pronta per essere eseguita.

Altri suggerimenti

Il tipo di loop che descrivi viene chiamato " wait wait " ;. Nei sistemi operativi reali, la sospensione non provoca un'attesa di occupato; indica al sistema operativo di non pianificare il processo fino al termine del periodo di sospensione.

Un meccanismo comune è utilizzare un select () che è garantito per il timeout e specificare il tempo di sospensione come timeout:

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

select () viene in genere utilizzato per controllare una serie di descrittori di file e attendere che almeno uno sia pronto per eseguire l'I / O. Se nessuno è pronto (o, in questo caso, se non viene specificato alcun file fds), scadrà.

Il vantaggio di select () su un loop occupato è che consuma pochissime risorse durante la sospensione, mentre un loop occupato monopolizza il processore tanto quanto consentito dal suo livello di priorità.

Non useresti il ??codice che hai pubblicato per dormire su un sistema incorporato. Un compilatore decente lo rimuoverà del tutto, e anche se il compilatore non lo rimuove non è ottimale, poiché l'esecuzione del processore in un ciclo stretto consumerà energia, il che è un problema per il sistema incorporato. Anche i sistemi che non utilizzano la batteria si preoccupano del consumo di energia, dal momento che un consumo di energia inferiore significa alimentatori e raffreddamento più economici.

Il modo in cui normalmente si fa è che la CPU implementerà una sorta di istruzioni IDLE o SLEEP, che causeranno l'interruzione temporanea dell'elaborazione dei comandi. Una linea di interruzione esterna collegata a un circuito del timer riattiverà il processore a intervalli regolari e quale punto controlla la CPU per vedere se è stato addormentato abbastanza a lungo e, in caso contrario, torna in modalità di sospensione.

//Pseudo code
int start = getTime();
int end = start + sleepTime;

while (getTime() < end) {
       asm("SLEEP");
}

I dettagli esatti variano da processore a processore. Se si esegue come processo su un sistema operativo, la chiamata di sospensione in genere dice semplicemente allo scheduler di sospendere il processo, quindi il kernel decide se pianificare un altro processo o sospendere la CPU. Inoltre, il codice sopra riportato non sarà adeguato per i sistemi in tempo reale, che richiedono garanzie di scadenza, ecc. In quei casi sarà necessario ottenere il tempo nel ciclo, conoscere la durata dell'interruzione del tempo in modo da sapere se è possibile riavviare senza scadere la scadenza e potenzialmente riprogrammare l'hardware del timer o attendere l'attesa.

Parli di " programmazione integrata " nell'OP. Se stai svolgendo un lavoro integrato e hai bisogno di qualcosa come sleep (), spesso sono disponibili contatori / timer hardware. Questo varierà da un'architettura all'altra, quindi dai un'occhiata al foglio dati.

Se non stai svolgendo un lavoro integrato, chiedo scusa :)

Se stai usando i for-loop, è meglio sapere in che cosa vengono compilati e quanto tempo impiegano queste istruzioni alla tua data velocità di clock, e assicurati che la CPU esegua le tue istruzioni e nient'altro (questo può essere fatto nei sistemi embedded ma è complicato poiché non consente gli interrupt).

Altrimenti, non sarai in grado di dire quanto tempo impiegherà davvero.

I primi giochi per PC avevano questo problema: erano costruiti per un PC a 4.7MHz e, quando arrivavano i computer più veloci, erano ingiocabili.

Il modo migliore in cui un 'sleep' può funzionare è che la CPU sappia che ora è in un dato punto. Non necessariamente l'ora effettiva (7:15) ma almeno il tempo relativo (8612 secondi da un certo momento).

In questo modo può applicare un delta all'ora corrente e attendere in un ciclo fino a raggiungere l'attuale + delta.

Tutto ciò che si basa su un numero di cicli della CPU è intrinsecamente inaffidabile in quanto la CPU può passare a un'altra attività e lasciare il ciclo sospeso.

Supponiamo che tu abbia una porta I / O a 16 bit mappata in memoria che la CPU incrementa una volta al secondo. Supponiamo anche che si trovi nella posizione di memoria 0x33 nel tuo sistema embedded, dove anche gli ints sono 16 bit. Una funzione chiamata sleep diventa quindi qualcosa del tipo:

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

Dovrai assicurarti che peek () restituisca ogni volta il contenuto della memoria (quindi ottimizzando i compilatori non confondere la logica) e che la tua istruzione while venga eseguita più di una volta al secondo in modo da non perdere l'obiettivo , ma questi sono problemi operativi che non influiscono sul concetto che sto presentando.

Ci sono ulteriori informazioni su come funziona sleep () qui

A proposito, l'attesa occupata non è necessariamente per i dilettanti, anche se brucia il processore che potresti voler usare per qualche altro scopo. Se si utilizza una fonte temporale, si è limitati alla granularità di tale fonte. PER ESEMPIO. se hai un timer da 1 ms e vuoi passare a 500 uS, hai un problema. Se il tuo sistema incorporato è in grado di gestire il fatto che stai ronzando in un ciclo per 500 uSec, ciò potrebbe essere accettabile. E anche se hai un timer con la granularità desiderata, devi anche interrompere un timer al momento giusto ... quindi inviare il gestore degli interrupt ... quindi ottenere il tuo codice. A volte un circuito affollato è la soluzione più conveniente. A volte.

L'attesa è per i dilettanti anche in un sistema incorporato, usa una fonte in tempo reale.

qualsiasi compilatore C decente eliminerebbe completamente il tuo codice senza ulteriore lavoro e il ritardo svanirebbe

sleep si interfaccia effettivamente con il sistema operativo, dove i processi di sleep sono collocati al di fuori della coda di pianificazione. Di solito uso:

poll(0, 0, milliseconds);

per sistemi conformi a POSIX. select funziona anche per Windows (per questo devono avere un'API nativa (probabilmente chiamata Sleep ).

La pagina 20 di "Threading in C #" di Joseph Albahari ne discute in modo interessante. Non è possibile dormire per meno di 1 ms in .Net ma DateTime.Ticks ha una granularità di intervalli di 100 nanosecondi (= 0,1 microsecondi). Per controllare il mio stepper CNC a 5 assi ho solo bisogno di fermarmi per 10 microsecondi tra i comandi passo. Ho usato un micro controller per fare il brutto loop ma penso che sia OK consegnare un processore per il lavoro se hai comunque un sacco di pezzi, ferma il thread quando puoi. Almeno non sarà sempre lo stesso.

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

Sì, usa alcune funzioni del kernel prive di documenti, ma onestamente, è molto più veloce della maggior parte dei suggerimenti e funziona molto bene (nota che sarà limitato alla quantità della CPU, ad esempio il mio può dormire solo 500 nanosecondi (0,5))

disponibile in Linux usleep (int microsecondi) nanosleep (...) più precisione vedi le pagine man per chiamare argomenti

In un sistema operativo derivato unix, probabilmente pianificheresti una chiamata signal () e il tuo codice semplicemente bloccherebbe il codice fino a quando il segnale non viene generato. I segnali sono destinati allo scopo e sono molto semplici ed efficienti.

un tentativo ... di risolvere davvero questo problema, ovvero qualcosa che funziona (non come i precedenti tentativi di risposta) lol

Devo ancora migliorare questo codice per renderlo chiaro. Pochi componenti aggiuntivi sono i benvenuti.

// 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;
}

Ho trovato la funzione in questo post ( http: //cboard.cprogramming.com/c-programming/111229-how-use-sleep-function.html ) e funziona:

#include <stdio.h>
#include <windows.h>

int main()
{
    puts("Hello \n");
    /* in windows.h is declared the Sleep (upper S) function and it takes time in 
miliseconds */
    Sleep(3000);
    puts("World \n");
    return 0;
}
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top