Appel en temps réel en veille ()?

J’ai écrit un utilitaire pour parler aux montres TomTom GPS via Bluetooth , et j’essaie de le faire fonctionner comme un démon d’arrière-plan run-and-forget.

Le programme communique périodiquement avec le périphérique GPS, puis sleep pendant un certain temps jusqu’à ce qu’il soit à nouveau nécessaire.

J’ai remarqué que sleep() interagit étrangement avec la suspension du système: lorsque j’interromps le système (ordinateur portable exécutant le kernel Linux 3.16.0) et que l’ordinateur redémarre, le mode sleep ne semble pas s’être arrêté. Par exemple, prenez le sleep.c suivant:

 #include  #include  #include  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)); } dlenski@dlenski-ultra:~$ 

Comstackr, exécuter et dormir à mi-chemin;

 $ gcc sleep.c -o sleep $ ./sleep 30 sleep at: Fri Aug 21 21:05:36 2015  wake at: Fri Aug 21 21:06:23 2015 

Existe-t-il un moyen correct de suspendre l’exécution du programme d’une manière qui tient compte de l’ horloge plutôt que de la disponibilité du système?

(J’ai aussi essayé le usleep , le nanosleep et l’ alarm et j’ai constaté qu’ils se comportaient de la même manière.)

MISE À JOUR: setitimer , comme suggéré par @HuStmpHrrr , semblait très prometteur … mais semble avoir le même problème.

Cette version fait une pause de plus de 30 secondes lorsque je suspends au milieu de celle-ci …

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

Quelques solutions et solutions potentielles:

Dormir en boucle et vérifier le temps réel écoulé

Il y a presque certainement une meilleure façon de le faire – ou il devrait y en avoir! – mais voici une solution acceptable pour mon application:

  • Lorsque vous devez dormir pendant un long intervalle (> 30 secondes), je sleep(30) plusieurs resockets, en vérifiant que le temps réel écoulé ( time(NULL)-start_time ) n’est pas plus long que la pause totale souhaitée.

  • De cette façon, si le système est suspendu pendant, par exemple, 3 heures, alors que la pause de programme souhaitée est de 1 heure, le délai supplémentaire causé par la suspension ne dépassera pas 30 secondes.

Code pour le faire:

 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 30) ? 30 : seconds-elapsed); signal(SIGALRM, SIG_IGN); if (res && verbose) fprintf(stderr, "%s\n\n", res ? " woken by signal!" : ""); return (res>0); } 

Rappel de réveil

@ Speed8ump a suggéré d’enregistrer un rappel pour les notifications de réactivation du système. Ce fil de discussion sur AskUbuntu montre comment le faire. Une bonne solution, mais je voudrais éviter de lier trop fortement mon code relativement bas à un environnement de bureau particulier pour le moment.

timer_settime

C’est la solution la plus élégante et la plus correcte, à mon avis, suggérée par @NominalAnimal . Il utilise time_settime() et les amis de librt .

Notez que pour que cela fonctionne correctement avec la suspension du système, vous devez utiliser CLOCK_REALTIME avec TIMER_ABSTIME , conformément à la documentation timer_settime :

Si la valeur de l’horloge CLOCK_REALTIME est ajustée alors qu’une timer absolue basée sur cette horloge est armée, alors l’expiration du temporisateur sera ajustée de manière appropriée. Les ajustements de l’horloge CLOCK_REALTIME n’ont aucun effet sur les temporisateurs relatifs basés sur cette horloge.

Voici une démo (lien avec librt , par exemple gcc -lrt -o sleep sleep.c ):

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