Newsgroup: it.comp.os.linux.development
Titolo: Domanda sulla "signal()" e handler del segnale

Ciao a tutti,
ho il seguente dubbio/problema: sto finendo di scrivere un esercizio per
l'Università abbastanza lungo e una delle ultime cose che mi rimangono è
la gestione di un segnale SIGUSR1 (poi, volendo, anche un SIGINT).
Il fatto è il seguente: il programma viene messo in attesa di un segnale
con
	signal(SIGUSR1, getout);
il cui controllo passa poi alla funzione (handler)
	void getout()
	{
		/* diverse cose da fare */
	}
Io avrei però bisogno di passare diversi parametri alla funzione
"getout()" (cosa che non so come fare, visto il prototipo "typedef void
(*sighandler_t)(int)") per il semplice fatto che essa deve eseguire ancora
parecchie operazioni (comprese operazioni di I/O su streams e file
descriptors aperti) prima di terminare il programma.
Un'idea che mi è venuta sarebbe quella di ridichiarare tutte le
variabili, le strutture e i puntatori a files nel corpo della funzione
"getout()" (ma mi sembra uno spreco di risorse assurdo, oltre che una
soluzione molto poco elegante), altrimenti pensavo di dichiarare tutte le
variabili che mi servono globalmente, togliendole dal "main()" (funzione
in cui compare la "signal()"), solo che anche questa soluzione non mi
convince per niente.
Io avrei bisogno di una cosa del genere:
	void getout(int n, FILE *fp, char foo)
	{
		/* operazioni varie su n passato dal main()... */
		/* operazioni varie su fp passato dal main()... */
		/* operazioni varie su foo passato dal main()... */
		exit(EXIT_SUCCESS);
	}
Si può in qualche modo fare una cosa simile (e possibilmente in una
maniera portabile su altri sistemi UNIX)? Vi ringrazio molto per
l'eventuale aiuto!
Ciao ciao :-)
-- 
      ______________
(*)--[::::::::::::::]--(*)
(*)--[>::d1s4st3r::<]--(*)
(*)--[::::::::::::::]--(*)
(*)--[::wonder'ing::]--(*)
(*)--[::what's::::::]--(*)
(*)--[::^happening::]--(*)
(*)--[::|^::::::::::]--(*)
(*)--[__||__________]--(*)

 convince per niente.
non so rispondere alla tua domanda, ma cosa c'è di male a dichiarare
globalmente le variabili che servono in più funzioni? io l'ho sempre fatto
e non sapevo che ci fossero controindicazioni.. ci sono?
-- 
[Filippo...........................]
[Linux User #342745................]
        ==[...penguin powered...]==

 Ciao ciao :-)
Ciao ciao
Myeawn

 e non sapevo che ci fossero controindicazioni.. ci sono?
Limitare lo scope di una variabile al solo blocco in cui la utilizzi può
facilitare la lettura del codice, in teoria.. poi ognuno ha il suo stile
Mrinco

 file descriptors aperti) prima di terminare il programma.
Occhio che le operazioni che puoi *legittimamente* fare in un signal
handler sono molto poche (quelle elencate nella relativa manpage di
signal(2)). 
In particolare, *NON* puoi far nulla che comporti (direttamente o
anche indirettamente) l'allocazione o la liberazione di memoria dinamica
nello heap.
Considera che la ricezione di un segnale (ed il relativo salto al signal
handler) possono avvenire in qualsiasi momento, ad esempio anche in mezzo
all'esecuzione di una una malloc() che sta modificando le sue strutture
interne, ed una eventuale malloc()/free() invocata dal signal handler si
ritroverebbe inevitabilmente con le relative strutture interne aggiornate
a metà (leggi: incoerenti), con risultati indefiniti.
Se il tuo programma passa il suo tempo in cicli di qualche genere
(probabile), tienti una variabile booleana globale, fai un signal handler
che si limita a modificarne il valore, e poi controllala periodicamente
nei cicli per vedere se è il caso di uscire (e di far quello che devi
fare).
Esempio banale (in C):
-----8<--------8<--------8<--------8<--------8<--------8<--------8<---
#include <stdio.h>
#include <signal.h>
static int received_SIGUSR1 = 0;
void on_sigusr1(int signumber) {
    received_SIGUSR1 = 1;
}
int main() {
     struct sigaction mysa;
     mysa.sa_handler = &on_sigusr1;
     sigemptyset(&mysa.sa_mask);
     mysa.sa_flags = 0;
     sigaction(SIGUSR1,&mysa,NULL);
 
     for(;;) {
 
	 printf("Faccio qualcosa... ");
         if ( received_SIGUSR1 ) {
             break; 
         }
     }
     printf("Basta.\n");
}
-----8<--------8<--------8<--------8<--------8<--------8<--------8<---
PS: personalmente preferisco sigaction(2) a signal(2) per la portabilità.
-- 
  |   \    \  | ___|_  |_  |  ianezz a casa sua... :-)
  |  _ \  | \ | _|    /   /   Verba volant, scripta
 _|_/  _\_|  _|____|___|___|  manent, data corrupted

 globalmente le variabili che servono in più funzioni?
Diventano parametri occulti alle funzioni, cosa che le rende più
difficili da comprendere e modificare: se si può evitare senza eccessivo
sforzo è meglio (per chi dovrà mettere le mani al tuo codice dopo di te,
e anche per te stesso dopo un mese che non lo guardi).


 attesa di un segnale dovrai ciclare un'attesa da qualche parte no ?
man 3 sleep - la CPU ringrazierà.
Barone Frankenstein
-- 
There is no place like /home.

 e non sapevo che ci fossero controindicazioni.. ci sono?
Parecchie. Nel caso specifico dei signal handler, niente garantisce che
mentre è in esecuzione l'handler di SIGUSR1 non arrivi un SIGUSR2 e se
l'handler di questo accede alle stesse variabili, si rischia un bug 
assai difficile da investigare.
Barone Frankenstein
-- 
Paranoia is an afterthought.

 convince per niente.
In un signal handler possono essere invocate solo funzioni classificate
come async-signal-safe: malloc(), ad esempio, non lo è e qualsiasi 
funzione che, *direttamente o indirettamente*, invochi malloc() non è 
ipso facto async-signal-safe. Siccome non c'è modo di sapere se una 
funzione, ora o nel 2011, invochi malloc(), le uniche funzioni che si
possono usare in un signal handler sono quelle per cui chi le scrive si 
premura di *garantire* che sono async-signal-safe - se non c'è tale
garanzia, non si possono usare e provare se funzionano non serve a niente.
Se basta farlo una volta sola, qualcosa del genere
   sig_atomic_t usr1_occurred = 0;
   sigaction(...); // meglio di signal(), imposta handler
   while (not_done_yet()) {
     sleep(A_SUITABLE_TIME_IN_SECONDS);
     ... possibly some stuff here
   }
   if (usr1_occurred) {
     ... stuff
   }
mentre nell'handler basta
   usr1_occurred = 1;
Se il comportamento one-shot non è adeguato, la faccenda si complica 
perchè sui segnali ci sono poche garanzie: il segnale può essere già
in coda quando viene impostato l'handler, che quindi viene eseguito
subito, ma può anche capitare che più segnali in sequenza collassino
in un'unica esecuzione dell'handler.


 man 3 sleep - la CPU ringrazierà.
Accidenti! E io che invece ero solito usare 
	for( a=0;a<1000000;a++);