View Full Version : [C] Timer periodico
Ciao a tutti,
nel nostro programma abbiamo bisogno di timer che scadano ad orari prestabiliti (per esempio la mezzanotte o le 3 del mattino) per inviare messaggi verso l'esterno.
Siamo su Linux e l'unico modo a me noto è di creare un thread con dentro un select() con il campo timeout settato a ora X - ora di adesso (quindi se ora sono le 18:14 la select dovrà durare circa 5 ore e 46 minuti) quando il timer scade facciamo le nostre cose e lo facciamo ripartire finché a Marzo alle 2 di notte scatta l'ora legale / solare e d'improvviso la mia select() non dovrebbe durare più 24 ore, ma 25 o 23! Se scatta alla una (il giorno dopo poi scatterà di nuovo alle 24 visto che ricalcola il tempo) potrebbe non essere tanto grave, ma se scatta alle 23 credendo che siano le 24 e poi di nuovo un ora dopo allora è male!
Insomma il funzionamento che vorrei è come quelle di una sveglia se la setto per suonare alle 8 del mattino e poi porto l'ora avanti suona alle 8 (anche se in realtà sono le 7) posso ottenere lo stesso con un PC da migliaia di dollari?
Linux sa per certo che c'è il DST l'orologio suo cambia (non solo quello della GUI, ma anche con il comando date) come posso ricevere un evento anche io?
C'è un modo per avere la funzionalità "sveglia" in Linux?
Grazie dell'aiuto!
P.S. quello delle 3 del mattino è ancora più satanico visto che è per mettere l'ora giusta ad una povera stampante da 4 soldi che non ha il concetto di DST... peccato quella è l'ora X e quindi lo sbagliamo sempre quel timer :Prrr:
Prova a dare un'occhiata alle funzioni timer_create e timer_settime.
http://man7.org/linux/man-pages/man2/timer_create.2.html
http://man7.org/linux/man-pages/man2/timer_settime.2.html
Assicurati di usare CLOCK_MONOTONIC_RAW quale base del clock, in questo modo non dovresti avere problemi di cambio orario by sysadmin o NTP.
OK domani provo a giocarci un po'!
Per essere chiaro io voglio che se l'oravenga cambiato il timer cambi... quindi non è mi chiara molto la nota su CLOCK_MONOTONIC_RAW.
Il mio timer deve scattare alle 24:00:00 tra 3:22:00 ore o tra 21 minuti se sposto l'orologio di sistema avanti di 2 ore!
Mi preoccupa un po' il fatto che si basi sui segnali visto che interrompono un po' il flusso dell'applicazione (è uno dei rari casi in cui il C non è più procedurale!) avevo però visto che c'era un modo per attendere un segnale in una select... così manterrei il main dei thread timer relativamente simile a come è adesso...
OK domani provo a giocarci un po'!
Per essere chiaro io voglio che se l'oravenga cambiato il timer cambi... quindi non è mi chiara molto la nota su CLOCK_MONOTONIC_RAW.
Il mio timer deve scattare alle 24:00:00 tra 3:22:00 ore o tra 21 minuti se sposto l'orologio di sistema avanti di 2 ore!
Mi preoccupa un po' il fatto che si basi sui segnali visto che interrompono un po' il flusso dell'applicazione (è uno dei rari casi in cui il C non è più procedurale!) avevo però visto che c'era un modo per attendere un segnale in una select... così manterrei il main dei thread timer relativamente simile a come è adesso...
Teoricamente (ammetto di non aver provato queste funzioni timer) non dovrebbe esserci bisogno di "cambiare il timer" in quanto queste funzioni usano il counter hardware del processore (CLOCK_MONOTONIC_RAW), che dovrebbe essere indipendente dall'RTC clock (CLOCK_REALTIME).
Per cui se in un certo istante il counter è a 1000 e imposti il timer per scattare a 1000+X, indipedentemente dall'ora di sistema lui dovrebbe scattare quando il counter raggiungerà il valore 1000+X.
Comunque è una questione interessante, domani se ho tempo provo anche io :D.
PS: in merito ai segnali credo che tu possa scegliere come fare il delivery dell'evento.
Allora ho finalmente risolto il problema (o quasi): la buona notizia è che timer_create() permette di creare 2 tipi di timer:
Timer Relativo: il classico timer che scade tra x secondi a partire da adesso
Timer Assoluti: timer che scade a un determinato orario ed è, quindi, sensibile ai cambiamenti di orologio
Visto che timer_settime() supporta la possibilità di ripetere periodicamente anche il timer ne ho ricavato facilmente un III tipo: Timer Periodico.
Quindi posso creare un timer per le 00:00:00 e poi spostare l'orologio della macchina con il comando date alle 23:59:59 e vederlo scattare dopo un secondo? Sì funziona come un cavallo :Prrr:
E se scatta l'ora legale dura un'ora in più? Assolutamente!
Restava un problema come detto sopra noi non volevamo realmente i segnali, visto l'imprendicibilità che hanno su quale thread vengono eseguiti fortunatamente se ne sono resi conto anche loro e quindi un timer può essere anche notificato in un thread ovvero allo scadere di ogni timer un nuovo thread viene creato (dal Kernel) e la callback fornita viene chiamata. Un po' un kludge come tanti che sono dentro le API C di Linux, ma sembra funzionare... per ora :eek:
Possibili difetti:
Non sono semplici da usare una funzione sola probabilmente bastava per far tutto, ma 2 ne sono richieste. Esiste anche timer_fd_create() che è meglio perché ti de l'illusione di usare un file su cui fare select(), read() ti indica che il timer è scaduto, close() termina il timer e rilascia le risorse... peccato che dobbiamo usare la CentOS 5.4 perché è "stabile" e così ha un kernel troppo vecchio
La struttura timespec usata è una cosa totalmente inutile ti promette risoluzione fino al nanosecondo, ma poi il kernel per lo switch dei processi si prende mediamente 2 millisecondi (così il mio timer periodico ogni 15 secondi a volte scade al secondo 15.001 altra al secondo 15.002 raramente accade a 15.000, ma non so se ci sono microsecondi dopo: non li stampo sono "rumore"). Avrebbe avuto più senso una struttura con secondi e millesecondi molto più realistico... quando mai serve un timer ogni nanosecondo? Tanto poi scadrebbe dopo 2 millisecondi quindi...
Dipendenza dal kernel potrebbe non essere portabile soprattutto se il kernel è troppo vecchio o se qualcuno si diverte a ricompilare il kernel rimuovendo cose "inutili".
Questo timer va cancellato con timer_delete(), ma nel thread che viene creato quando scade manca l'id del thread stesso, puoi passarci una struttura o un numero a caso, ma non l'idvisto che è un valore ritornato dalla timer_create() stessa... quindi bisogna recuperarlo in qualche modo... (noi mettiamo i timer creati dentro una coda con chiave l'id "simbolico" che passiamo al thread e ovviamente il timer_id che timer_create() ci ritorna così poi sul thread dopo aver inviato al processo richiedente la notifica lo cerchiamo dentro la coda e lo facciamo "secco")
Ah detesto profondamente come il C rappresenta il tempo:
http://pubs.opengroup.org/onlinepubs/007908775/xsh/time.h.html
time_t, struct tm, struct timespec, 5 o 6 orologi (CLOCK_REALTIME, CLOCK_MONOTONIC, CLOCK_???).
Un problema resta: l'ora legale è un limite satanico che non può essere traversato per esempio io posso fare un timer fino alla notte in cui le 2 di notte diventano le 3, ma ho fatto una scoperta aberrante le 2 di notte avvengono 2 volte quella notte :cry:
L'implementazione attuale è un po' farlocca quando l'ora è solare c'è tutte le notte un timer fino alle 3.00 che quando scade verifica se, in realtà, sono le 2.00 e allora va a menarlo alla povera stampante che chissà perché pretende di avere un orologio, ma, poi, non sa gestire il DST, quando l'ora è legale il timer diventa tutte le notti alle 2.00. Ma ciò che vorremmo veramente sapere è "se ora c'è l'ora solare quando dovrà essere cambiata nell'ora legale?" e allora solo "in quei giorni" (sembra una pubblicità dei salva-slip detta così :Prrr: ) settare il nostro timer ovvero per solo 2 volte all'anno.
Una possibile soluzione sarebbe di parsarsi il file in /etc/timezone/xxx e leggersi da lì quando sono "quei giorni" anche se preferirei che il kernel mi avvisasse in qualche modo con un evento...
non fai prima a creare il tuo programma che cambia solamente l'ora e far gestire il lancio del programma direttamente al sistema operativo mediante cron ?
No, non posso usare Crontab l'applicazione deve fare altre cose nel momento del cambio dell'ora legale / solare oltre a settare l'ora alla stampante e un programma esterno non potrebbe certo accedere alle sue variabili esterne.
Devo quindi trovare un modo per cui capisca da solo quando si deve cambiare l'ora legale / solare al limite mi parserò quel file mi scoccia un po' perché duplico il lavoro che Linux sta già facendo, ma se, davvero, non c'è modo per essere informato di questo evento...
cdimauro
08-12-2015, 06:20
Ma è proprio necessario che siano le 3 in punto? Non potresti aspettare 1 o 2 secondi dopo le 3, di modo che sia sicuro che il cambio dell'ora sia già avvenuto?
No, non posso usare Crontab l'applicazione deve fare altre cose nel momento del cambio dell'ora legale / solare oltre a settare l'ora alla stampante e un programma esterno non potrebbe certo accedere alle sue variabili esterne.
Devo quindi trovare un modo per cui capisca da solo quando si deve cambiare l'ora legale / solare al limite mi parserò quel file mi scoccia un po' perché duplico il lavoro che Linux sta già facendo, ma se, davvero, non c'è modo per essere informato di questo evento...
Secondo me l'idea di cron non è male invece.
In cron ti metti uno script che lancia un signal al tuo programma , tipo un SIGUSR1. E se proprio non vuoi duplicare troppo puoi gestirlo anche solo per la gestione dell'ora legale.
Ma è proprio necessario che siano le 3 in punto? Non potresti aspettare 1 o 2 secondi dopo le 3, di modo che sia sicuro che il cambio dell'ora sia già avvenuto?
Purtroppo deve scattare alle 3 in punto: rivelo un po' che cos'è la mia "applicazione" intanto non è un solo programma, ma più di uno che comunicano attraverso code real time tra di loro, il sistema realizzato è un sistema di esazione che è aperto H24 (chiuso solo per guasti hardware di fatto) quindi se passa una persona e, per disgrazia, arriva nel momento del cambio dell'ora deve avere lo scontrino con l'ora esatta quindi no, non posso attendere nemmeno pochi secondi!
@Kaya
Quindi anche la soluzione del segnale non è possibile a quale processo lo manderei? L'intelligenza, purtroppo deve rimanere all'interno dell'applicazione distribuita...
cdimauro
09-12-2015, 19:34
Allora verifica periodicamente, ogni secondo o anche meno se ti è possibile, l'orario corrente. Conserva il precedente e controlla, quando scatta il nuovo evento, se la differenza fra l'attuale e il precedente è negativa (perché il problema si pone solo quando l'orologio torna indietro, immagino) e di gran lunga più elevata rispetto a ciò che ti aspetti normalmente. A questo punto dovresti essere relativamente sicuro di aver beccato proprio il cambio, e propaghi la notizia a tutti i dispositivi.
se hai un kernel relativamente recente puoi ottenere la notifica del cambio dell'ora (personalmente non l'ho testata, YMMV):
https://lkml.org/lkml/2010/9/16/405
Questa time_change_notify() sembra proprio quello che sto cercando, purtroppo il mio kernel è vecchissimo (2.6.18 del 2009!), quindi mi sa che non c'è la cosa strana è che anche su un kernel 4.0 non la trovo (non c'è nessun man e in /usr/include non trovo la definizione).
Vedo però che l'esempio postato la chiama attraverso una syscall quindi ho ancora una flebile speranza... devo provare a compilarlo :D
vBulletin® v3.6.4, Copyright ©2000-2025, Jelsoft Enterprises Ltd.