Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Ogni 10 secondi un'operazione

0 views
Skip to first unread message

pozz

unread,
Jul 9, 2008, 6:01:43 PM7/9/08
to
Devo eseguire ogni 10 secondi una determinata operazione, non molto
lunga (si tratta di aggiungere in un file di log un dato che chiedo
sulla porta seriale).

La cadenza di 10 secondi deve essere di questo tipo:
00:00:00
00:00:10
00:00:20
...
e non
00:00:03
00:00:13
00:00:23
...
Questo indipendentemente dall'istante in cui il programma viene lanciato
(cioè se viene messo in esecuzione alle 00:00:03, la prima operazione
verrà effettuata alle 00:00:10).

Siccome l'intervallo tra un'operazione e l'altro è dell'ordine dei
secondi, non posso usare cron che, da quello che ho capito, gestisce
risoluzioni fino al minuto e non meno.

Non posso neanche utilizzare delle pause di 10 secondi tra un'operazione
e l'altra perchè, dopo un po' di tempo (l'applicazione dovrà funzionare
ininterrottamente per mesi/anni), ci sarà un disallineamento tra gli
istanti teorici e quelli reali. In altre parole potrei avere qualcosa di
questo tipo:
00:00:00
00:00:10
...
06:59:50
07:00:01
07:00:11
...

Devo, quindi, per forza legarmi alla data corrente del sistema (tenuta
sincronizzata con un orario di riferimento preciso in altro modo).
A questo punto mi vengono in mente due strade.

All'esecuzione richiedo il tempo attuale mediante la funzione time() che
mi ritorna un numero che rappresenta il numero dei secondi da una data
di riferimento remota. Tolgo il resto della divisione per 10 e sommo 10
per ottenere il successivo istante in cui devo fare l'operazione.
Attendo fino quando il valore di time() sia maggiore di questo istante
atteso.
---
t = time ();
t = 10 * (t / 10);
while (time () < t)
sleep (FD_SETSIZE, NULL, NULL, NULL, tv);
---
Per evitare di impegnare continuamente la CPU con la funzione time(),
mediante la sleep() mi metto in attesa per mezzo secondo.
Cosa succede, però, se si cambia la data di sistema mettendola un anno
prima, per esempio? Il programma rimane bloccato per un anno ad
aspettare quello che lui crede essere l'istante successivo dell'operazione.
Per risolvere questo problema dovrei ricordarmi la data precedente e, se
troppo diversa, ricominciare...

Un altro sistema è quello di leggere continuamente l'istante time() fin
quando il suo valore è divisibile per 10.
---
while (time() % 10)
;
<operazione>
---
Se l'operazione è troppo breve, rischio di eseguirla più volte per lo
stesso secondo, quindi:
---
old_t = 0;
while (((t = time()) % 10) && (10 * (t / 10) != old_t))
;
t = 10 * (t / 10);
old_t = t;
<operazione>
---
Questo procedimento funzionerebbe tranquillamente anche se la data di
sistema viene cambiata ad un valore molto diverso da quello attuale.
Ma che succede se la CPU è impegnata durante il secondo divisibile per
10? Non rischio di saltare un'operazione in questo caso? Nel primo
metodo ciò non succedeva perchè facevo un test di disuguaglianza e non
di uguaglianza come in questo caso.
Inoltre mi secca dover fare quel ciclo while() di attesa che impegna
risorse della CPU (con la funzione time() e qualche calcolo)
semplicemente per aspettare un dato istante noto.

Vi vengono in mente altre soluzioni?

Luca Pascali

unread,
Jul 10, 2008, 3:05:20 AM7/10/08
to
pozz wrote:
> Devo eseguire ogni 10 secondi una determinata operazione, non molto
> lunga (si tratta di aggiungere in un file di log un dato che chiedo
> sulla porta seriale).
>
> La cadenza di 10 secondi deve essere di questo tipo:
> 00:00:00
> 00:00:10
> 00:00:20
[...]

>
> Vi vengono in mente altre soluzioni?

taglio un po' tutto.

Innanzitutto, ciclare con una sleep a mezzo secondo (o anche ad un
secondo, se non hai bisogno di grossa precisione) va bene.

Cambierei la condizione di innesco, però.

I secondi 10,20,30, ecc. sono nient'altro che (time()%10)==0
Quindi farei un ciclo del tipo

while (true) {
struct timeval tv;

tv.tv_sec = 0;
tv.tv_usec = 500000; //500 ms
select (FD_SETSIZE,NULL,NULL,NULL,&tv);

if (time()%10!=0) {
//Non ci interessa, aspettiamo altri 500ms
continue;
}

//Qui eseguiamo l'operazione, invocare break per uscire
<operazione>

//Dobbiamo essere sicuri di non eseguire
//due volte nello stesso secondo, quindi
//aspettiamo 2 secondi (ne avremmo altri 8)
sleep(2);
}

Alternativamente al break per uscire dal ciclo while, puoi definire una
variabile intera (fgKill ad esempio) impostata a 0 all'inizio che viene
messa a 1 quando vuoi che l'esecuzione sia l'ultima.
Il ciclo while diventa quindi

while (fgKill==0) {
.....
}

e un po' più pulito.

PSK

Giovanni

unread,
Jul 10, 2008, 3:27:14 AM7/10/08
to
On 07/10/08 00:01, pozz wrote:
> Devo eseguire ogni 10 secondi una determinata operazione, non molto
> lunga (si tratta di aggiungere in un file di log un dato che chiedo
> sulla porta seriale).
> ------------- [ cut ] ---------------------------

> Vi vengono in mente altre soluzioni?

Puoi provare ad usare /dev/rtc. La documentazione nel file rtc.txt
(nella directory Documentation del kernel) è scarna, ma IMO puoi fare
quello che ti serve.

Ciao
Giovanni
--
A computer is like an air conditioner,
it stops working when you open Windows.
Registered Linux user #337974 < http://giovanni.homelinux.net/ >

pozz

unread,
Jul 10, 2008, 3:56:27 AM7/10/08
to
On 10 Lug, 09:05, Luca Pascali <pasckoskyNOS...@TOGLIMI.yahoo.iTt>
wrote:

> Cambierei la condizione di innesco, però.
>
> I secondi 10,20,30, ecc. sono nient'altro che (time()%10)==0

Sì, è praticamente la mia seconda soluzione che va a guardare il resto
della divisione di time() con 10.


> while (true) {
> struct timeval tv;
>
> tv.tv_sec = 0;
> tv.tv_usec = 500000; //500 ms
> select (FD_SETSIZE,NULL,NULL,NULL,&tv);
>
> if (time()%10!=0) {
> //Non ci interessa, aspettiamo altri 500ms
> continue;
> }
>
> //Qui eseguiamo l'operazione, invocare break per uscire
> <operazione>
>
> //Dobbiamo essere sicuri di non eseguire
> //due volte nello stesso secondo, quindi
> //aspettiamo 2 secondi (ne avremmo altri 8)
> sleep(2);
>
> }

Tu hai risolto il problema di non eseguire più volte la stessa
operazione
per lo stesso secondo con la sleep(2), io l'avevo risolto con old_t.
Effettivamente con il tuo metodo evito una variabile e metto in pausa
il
processo liberando la CPU.
Ma direi che è meglio usare la select(), piuttosto che la sleep.

Il problema di questo metodo, come dicevo nell'OP, è che, se la CPU
è particolarmente impegnata proprio durante il secondo "buono" (il
tutto gira su un'architettura
molto minimale), rischio di saltare l'operazione per quel secondo.

Alessandro Pellizzari

unread,
Jul 10, 2008, 4:23:24 AM7/10/08
to
Il Wed, 09 Jul 2008 22:01:43 +0000, pozz ha scritto:

> t = time ();
> t = 10 * (t / 10);
> while (time () < t)
> sleep (FD_SETSIZE, NULL, NULL, NULL, tv);

> Vi vengono in mente altre soluzioni?

Non sono espertissimo, ma secondo me ti conviene usare setitimer() oppure
alert(), e poi ti metti in attesa del segnale ALRM.
Con setitimer() mi pare che il timer ricominci da solo dopo la scadenza,
ma forse e` meglio se leggi il manuale. :)

In questo modo il processo si risveglierebbe solo quando riceve il segnale
(ogni 10 secondi) invece che ogni mezzo secondo, con il vantaggio di
lasciare la macchina piu` scarica, e in piu` che la sua priorita`
aumenterebbe e quindi verrebbe eseguito piu` rapidamente allo scadere del
timer.

Bye.

pozz

unread,
Jul 10, 2008, 9:23:49 AM7/10/08
to
On 10 Lug, 10:23, Alessandro Pellizzari <shuri...@amiran.it> wrote:
> Non sono espertissimo, ma secondo me ti conviene usare setitimer() oppure
> alert(), e poi ti metti in attesa del segnale ALRM.

Il problema di setitimer() è che si basa solo sul clock di sistema che
è svincolato
dall'RTC. Dopo un anno di funzionamento, è molto probabile che i tempi
in cui farei
le operazioni non sono più


00:00:00
00:00:10
...

ma
00:00:01
00:00:11
...
cioè se il clock di sistema (cioè l'interrupt di 1 secondo che
gestisce le pause tra cui
quelle di setitimer()) va più lentamente dell'orologio.

Per evitare ciò devo per forza legare l'esecuzione dell'operazione al
clock di sistema che
mi preoccuperei di mantenere sincronizzato.

Dildo Bogumil di Boscopelo

unread,
Jul 10, 2008, 9:44:29 AM7/10/08
to
pozz wrote:

> Vi vengono in mente altre soluzioni?

il fatto è che in un sistema non real-time non credo ci sia la possibilità di
garantire quello che tu chiedi.


--
SF

Games are very educational. Scrabble teaches us vocabulary, Monopoly teaches
us cash-flow management, and Dungeons & Dragons teaches us to loot dead bodies.

Alessandro Pellizzari

unread,
Jul 10, 2008, 9:49:53 AM7/10/08
to
Il Thu, 10 Jul 2008 06:23:49 -0700, pozz ha scritto:

> Il problema di setitimer() è che si basa solo sul clock di sistema che è
> svincolato
> dall'RTC. Dopo un anno di funzionamento, è molto probabile che i tempi
> in cui farei
> le operazioni non sono più

Ogni tot operazioni (un migliaio) distruggi il timer precedente e lo
reimposti in base al RTC? :)

Oppure intercetti un altro segnale (SIG1) che fa questa operazione, e
quando sincronizzi il clock mandi quel segnale alla tua applicazione che
si reimposta.

La butto li`, non ho mai fatto niente del genere.

Bye.

pozz

unread,
Jul 10, 2008, 4:46:29 PM7/10/08
to
On 10 Lug, 15:44, Dildo Bogumil di Boscopelo <psycho...@libero.it>
wrote:

> il fatto è che in un sistema non real-time non credo ci sia la possibilità di
> garantire quello che tu chiedi.

No, il mio obiettivo non è quello di essere preciso con i tempi, cosa
importante
nei sistemi operativi real-time.
Ogni tanto potrei anche eseguire l'operazione 1-2 secondi dopo
l'istante teorico se
il sistema è occupato, quindi non mi interessano delle risposte di
tipo real-time.

La cosa importante è che mediamente io vada ad eseguire l'operazione
ogni
10 secondi. Cioè dopo 10 anni di funzionamento (vabbè, ho esagerato)
l'istante
dell'operazione sia sempre vicino all'istante teorico.

Roberto C

unread,
Jul 10, 2008, 5:38:34 PM7/10/08
to
pozz ha scritto:

> Devo eseguire ogni 10 secondi una determinata operazione, non molto
> lunga (si tratta di aggiungere in un file di log un dato che chiedo
> sulla porta seriale).

> La cadenza di 10 secondi deve essere di questo tipo:
> 00:00:00
> 00:00:10
> 00:00:20
> ...
> e non
> 00:00:03
> 00:00:13
> 00:00:23
> ...
> Questo indipendentemente dall'istante in cui il programma viene lanciato

> (cioč se viene messo in esecuzione alle 00:00:03, la prima operazione
> verrŕ effettuata alle 00:00:10).

ricavo dal codice che tempo fa ho postato nel NG del Perl
http://groups.google.it/group/it.comp.lang.perl/msg/c5b151eed7d4f2ca?hl=it&dmode=source

Una modifica che forse fa per te.

da quello che ho capito devi correggere gli eventuali scostamenti
dall' ascissa temporale, per essere sempre in fase con dei punti cardinali.
Per quanto possano essere precisi gli orologi, tali sistemi implicano la
periodica correzione dell' errore.

un esempio in Perl:

--- file temporizzatore.pl ---
#!/usr/bin/perl

sub temporizzata
{
$dataora = gmtime(time());
print "\n",$dataora,"n";
$SIG{'ALRM'} = \&temporizzata;
alarm(10 - (time() % 10));
}

$SIG{'ALRM'} = \&temporizzata;
alarm(10 - (time() % 10));
$inputstring = <STDIN>;

_END_

--- fine file temporizzatore.pl ---

lo esegui con
perl temporizzatore.pl

e lo fermi premendo ENTER o con ^C

Usa la combinazione signal() e alarm() e puoi semplicemente
tradurlo in C o adattarlo ai tuoi scopi.

Il mio suggerimento e' tutto in:

alarm(10 - (time() % 10));

che si riassume in "sistema, mandami un SIGALRM tra il tempo
congruo a time attuale + 10 secondi in modo che possa eseguire
la subroutine temporizzata"

E' questo che chiedevi?

Ciao
Roberto.


--

questo articolo e` stato inviato via web dal servizio gratuito
http://www.newsland.it/news segnala gli abusi ad ab...@newsland.it


Roberto C

unread,
Jul 10, 2008, 5:45:23 PM7/10/08
to
Roberto C ha scritto:

....

> print "\n",$dataora,"n";

Come al solito newsland.it mi ha eliminato una \

doveva essere:

print "\n",$dataora,"\n";

Roberto C

unread,
Jul 10, 2008, 5:56:21 PM7/10/08
to
Roberto C ha scritto:

....

> Il mio suggerimento e' tutto in:

> alarm(10 - (time() % 10));

> che si riassume in "sistema, mandami un SIGALRM tra il tempo
> congruo a time attuale + 10 secondi in modo che possa eseguire
> la subroutine temporizzata"

> E' questo che chiedevi?

A pensarci bene, sleep() e' implementata con SIGALRM come dice

man 3 sleep

(potrebbe ...)

quindi, visto che tu l' hai suggerita, il principio si puo' applicare
anche all' approccio con sleep:

perl -e 'while(1){sleep(10 - (time() % 10)); print "\n",time(),"\n"}'

Roberto C

unread,
Jul 10, 2008, 6:05:41 PM7/10/08
to
pozz ha scritto:

....

> All'esecuzione richiedo il tempo attuale mediante la funzione time() che
> mi ritorna un numero che rappresenta il numero dei secondi da una data
> di riferimento remota. Tolgo il resto della divisione per 10 e sommo 10
> per ottenere il successivo istante in cui devo fare l'operazione.
> Attendo fino quando il valore di time() sia maggiore di questo istante
> atteso.

....

lo avevi gia' fatto. quindi non ho capito il problema.
Scusate.

pozz

unread,
Jul 13, 2008, 9:03:55 AM7/13/08
to
Roberto C ha scritto:

>> All'esecuzione richiedo il tempo attuale mediante la funzione time() che
>> mi ritorna un numero che rappresenta il numero dei secondi da una data
>> di riferimento remota. Tolgo il resto della divisione per 10 e sommo 10
>> per ottenere il successivo istante in cui devo fare l'operazione.
>> Attendo fino quando il valore di time() sia maggiore di questo istante
>> atteso.
> lo avevi gia' fatto. quindi non ho capito il problema.

Il problema è nel cambio data. Se, mentre aspetto l'istante "01/01/2009
00:00:00", qualcuno cambia data al "01/01/2008 00:00:00", il mio
programma è bloccato per un anno!

Penso che utilizzerò sleep() per addormentare (quindi senza caricare la
CPU) il processo un numero di secondi pari a quello rimanente per
arrivare al successivo istante.
while (1) {
t = time ();
if (t % 10)
sleep (10 * (t / 10) + 10 - t);
<eseguo l'operazione>
sleep (1);
}
Sicuramente, con il primo sleep(), farò un'attesa che varia da 0 a 9
secondi, indipendentemente dall'eventuale cambiamento di data.

Dopo l'operazione faccio un'altra sleep() di 1 secondo per evitare di
rifare l'operazione più volte per lo stesso secondo utile.
E' una cosa che non mi piace di questo sistema, ma dovrebbe funzionare.

Roberto C

unread,
Jul 14, 2008, 3:34:47 AM7/14/08
to
pozz ha scritto:


> Il problema č nel cambio data. Se, mentre aspetto l'istante "01/01/2009

> 00:00:00", qualcuno cambia data al "01/01/2008 00:00:00", il mio

> programma č bloccato per un anno!

ripropongo:


perl -e 'while(1){sleep(10 - (time() % 10)); print "n",time(),"n"}'

non mi pare che sia soggetto al problema che dici. Sleep appetta MAX
10 secondi anche se qualcuno cambia date o hwclock e al riarmo viene
sempre passato un argomento che non sara' mai > di 10, semmai minore
della quantita' necessaria alla correzione.

> Penso che utilizzerň sleep() per addormentare (quindi senza caricare la

> CPU) il processo un numero di secondi pari a quello rimanente per
> arrivare al successivo istante.
> while (1) {
> t = time ();
> if (t % 10)

Lo traduco come:
se il tempo assoluto dal 1970 non e' congruo ai 10 secondi..

> sleep (10 * (t / 10) + 10 - t);

. calcolo quanti secondi necessari per renderlo congruo ai 10 secondi ...
(ma perche' non usare t % 10 ???)

> <eseguo l'operazione>
> sleep (1);
> }

> Sicuramente, con il primo sleep(), farň un'attesa che varia da 0 a 9

> secondi, indipendentemente dall'eventuale cambiamento di data.

> Dopo l'operazione faccio un'altra sleep() di 1 secondo per evitare di

> rifare l'operazione piů volte per lo stesso secondo utile.


> E' una cosa che non mi piace di questo sistema, ma dovrebbe funzionare.

Ad essere sincero non piace neanche a me :)

Ciao
Roberto.

pozz

unread,
Jul 14, 2008, 1:25:14 PM7/14/08
to
Roberto C ha scritto:
>> Il problema è nel cambio data. Se, mentre aspetto l'istante "01/01/2009
>> 00:00:00", qualcuno cambia data al "01/01/2008 00:00:00", il mio
>> programma è bloccato per un anno!

>
> ripropongo:
> perl -e 'while(1){sleep(10 - (time() % 10)); print "n",time(),"n"}'

Questo funzionicchia, nel senso che ti manca la seconda sleep().
Se non la metti, sicuramente all'istante "congruo" esegui molte volte
l'operazione che va eseguita, invece, solo una volta (immagino che la
print venga eseguita in una manciata di millisecondi).


> non mi pare che sia soggetto al problema che dici. Sleep appetta MAX
> 10 secondi anche se qualcuno cambia date o hwclock e al riarmo viene
> sempre passato un argomento che non sara' mai > di 10, semmai minore
> della quantita' necessaria alla correzione.

Sì, questo funziona al cambio data. Quello che non funziona è aspettare
un istante ben preciso, del tipo:
t = time ();
t_new = t + 10 - (t % 10);
while (time () < t_new)
;


>> Penso che utilizzerò sleep() per addormentare (quindi senza caricare la

>> CPU) il processo un numero di secondi pari a quello rimanente per
>> arrivare al successivo istante.
>> while (1) {
>> t = time ();
>> if (t % 10)
> Lo traduco come:
> se il tempo assoluto dal 1970 non e' congruo ai 10 secondi..
>
>> sleep (10 * (t / 10) + 10 - t);
> . calcolo quanti secondi necessari per renderlo congruo ai 10 secondi ...
> (ma perche' non usare t % 10 ???)

Hai ragione...


>> <eseguo l'operazione>
>> sleep (1);
>> }

>> Sicuramente, con il primo sleep(), farò un'attesa che varia da 0 a 9

>> secondi, indipendentemente dall'eventuale cambiamento di data.
>
>> Dopo l'operazione faccio un'altra sleep() di 1 secondo per evitare di

>> rifare l'operazione più volte per lo stesso secondo utile.


>> E' una cosa che non mi piace di questo sistema, ma dovrebbe funzionare.
> Ad essere sincero non piace neanche a me :)

E vabbè, ma non ci sono alternative, mi sembra...

0 new messages