2015-08-22 63 views
9

Ho scritto un'utilità per parlare con TomTom GPS watches over Bluetooth e sto provando a farlo funzionare bene come un demone di sfondo run-and-forget.Chiamata sleep in tempo reale() in tempo reale?

Il programma comunica periodicamente con il dispositivo GPS e quindi sleep s per un po 'finché non è necessario nuovamente.

Ho notato che sleep() interagisce stranamente con il sistema di sospensione: quando entro sistema di sospensione (laptop con Linux 3.16.0 kernel) e quindi riattivare il computer di backup, sleep non sembra notare il tempo di sospensione. Per esempio, prendete il seguente sleep.c:

#include <time.h> 
#include <stdio.h> 
#include <unistd.h> 

int main(int argc, char**argv) 
{ 
    time_t t=time(NULL); 
    printf("sleep at: %s", ctime(&t)); 

    sleep(atoi(argv[1])); 

    t=time(NULL); 
    printf("wake at: %s", ctime(&t)); 
} 
[email protected]:~$ 

compilare, eseguire, e il sonno a metà;

$ gcc sleep.c -o sleep 
$ ./sleep 30 
sleep at: Fri Aug 21 21:05:36 2015 
<suspend computer for 17 seconds> 
wake at: Fri Aug 21 21:06:23 2015 

Esiste un modo corretto di sospendere l'esecuzione del programma in un modo che è orologio in tempo -consapevoli piuttosto che sistema uptime -consapevoli?

(ho anche provato usleep, nanosleep e alarm e ha scoperto che si comportano in modo simile.)

UPDATE:setitimer, come suggerito da @HuStmpHrrr, sembrava abbastanza promettente ... ma sembra avere la stesso problema.

Questa versione mette in pausa per più di 30s richiesti quando sospendo nel mezzo di esso ...

#include <time.h> 
#include <stdio.h> 
#include <unistd.h> 
#include <signal.h> 
#include <sys/time.h> 

void sighandler() {} 

int main(int argc, char**argv) 
{ 
    time_t t=time(NULL); 
    printf("sleep at: %s", ctime(&t)); 

    signal(SIGALRM, sighandler); 
    struct itimerval timer = {.it_interval={0,0}, 
           .it_value={atoi(argv[1]),0}}; 
    setitimer(ITIMER_REAL, &timer, NULL); 
    pause(); 

    t=time(NULL); 
    printf("wake at: %s", ctime(&t)); 
} 
+0

che dire 'alarm'? 'alarm() ordina che un segnale SIGALRM venga consegnato al processo chiamante in secondi secondi. leggi promettenti. – HuStmpHrrr

+1

'setitimer' fornisce più controllo. puoi passare in "ITIMER_REAL" per ottenere ciò che desideri. – HuStmpHrrr

+0

'setitimer' sembra molto promettente ma sembra avere lo stesso problema in quanto non conta" tempi di inattività ". Polling 'time' sembra una soluzione abbastanza inelegante ma probabilmente quella giusta :( –

risposta

5

alcune soluzioni e le possibili soluzioni:

Dormire in un ciclo e verificare la reale il tempo trascorso

c'è quasi certamente un modo migliore per farlo, o ci dovrebbe essere -ma qui è una soluzione accettabile per la mia applicazione per il momento:

  • Quando il bisogno di dormire per un lungo intervallo di tempo (> 30 anni), lo faccio sleep(30) più volte, controllando che il vera tempo trascorso (time(NULL)-start_time) non è più lungo di totale desiderata della pausa.

  • In questo modo, se il sistema è sospeso per, diciamo, 3 ore, mentre la sospensione del programma desiderata è di 1 ora, il ritardo aggiuntivo causato dalla sospensione non supererà i 30 secondi.

codice per farlo:

void nullhandler(int signal) {} 

int isleep(int seconds, int verbose) 
{ 
    if (verbose) { 
     fprintf(stderr, "Sleeping for %d seconds...", seconds); 
     fflush(stderr); 
    } 
    signal(SIGALRM, nullhandler); 

    // weird workaround for non-realtime-awareness of sleep: 
    // https://stackoverflow.com/questions/32152276/real-time-aware-sleep-call 
    int res=0, elapsed=0; 
    for (time_t t=time(NULL); (elapsed<seconds) && (res<=0); elapsed=time(NULL)-t) 
     res = sleep((seconds-elapsed > 30) ? 30 : seconds-elapsed); 

    signal(SIGALRM, SIG_IGN); 
    if (res && verbose) 
     fprintf(stderr, "%s\n\n", res ? " woken by signal!" : ""); 
    return (res>0); 
} 

Wakeup callback

@Speed8ump suggerito la registrazione di un callback per le notifiche di sistema-sveglia. This thread over at AskUbuntu mostra come farlo. Una buona soluzione, ma mi piacerebbe evitare di legare troppo forte il mio codice di livello relativamente basso a un particolare ambiente desktop per ora.

timer_settime

Questa è la soluzione più elegante e corretto, a mio parere, ed è stato suggerito da @NominalAnimal. Utilizza time_settime() e gli amici da librt.

Nota che per fare questo lavoro in modo corretto con il sistema di sospensione, è necessario utilizzare CLOCK_REALTIME con TIMER_ABSTIME, per la documentazione timer_settime:

Se il valore dell'orologio CLOCK_REALTIME è regolato mentre basa un timer assoluto su quel orologio è armato, quindi la scadenza del timer verrà regolata in modo appropriato. Le regolazioni dell'orologio CLOCK_REALTIME non hanno alcun effetto sui timer relativi basati su tale orologio.

Ecco una demo (link con librt, per esempio gcc -lrt -o sleep sleep.c):

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

void sighandler(int signal) {} 

void isleep(int seconds) 
{ 
    timer_t timerid; 
    struct sigevent sev = { .sigev_notify=SIGEV_SIGNAL, 
          .sigev_signo=SIGALRM, 
          .sigev_value=(union sigval){ .sival_ptr = &timerid } }; 
    signal(SIGALRM, sighandler); 
    timer_create(CLOCK_REALTIME, &sev, &timerid); 

    struct itimerspec its = {.it_interval={0,0}}; 
    clock_gettime(CLOCK_REALTIME, &its.it_value); 
    its.it_value.tv_sec += seconds; 
    timer_settime(timerid, TIMER_ABSTIME, &its, NULL); 
    pause(); 
    signal(SIGALRM, SIG_IGN); 
} 

int main(int argc, char**argv) 
{ 
    time_t t=time(NULL); 
    printf("sleep at: %s", ctime(&t)); 

    isleep(atoi(argv[1])); 

    t=time(NULL); 
    printf("wake at: %s", ctime(&t)); 
}