PDA

View Full Version : [C] Grosso dubbio con la malloc...


Ed_Bunker
13-07-2004, 11:53
[C] Allocazione dinamica...
Ciao, ho un problemuccio urgente da capire...
Si tratta di realizzare un semplice server che ad ogni
richiesta ricevuta mediante pipe crei u proceso ad hoc
per svolgere una certa funzione calcolando il tempo di
esecuzione impiegato dal processo figlio. In partciolare
dovrei fare qualcosa di questo genere:

...
struct timeval * start;
struct timeval * end;
long int med;
...
int main(int argc, char * argv[])
{
...
/*Per gestire il sigchld...*/
struct sigaction new, old;
new.sa_handler = deadHandler;
if(sigaction(SIGCHLD, &new, &old) == -1)
{
perror("Errore nella sigaction");
exit(-1);
}

while (TRUE)
{
/*RICEZIONE DELLE RICHIESTE*/
int pid;
start = (struct timeval*) malloc (sizeof(struct timeval));end = (struct timeval*) malloc (sizeof(struct timeval));
gettimeofday(start,NULL);
pid = fork();
if (pid ==0)/*Figlio*/
{
funzione1(...qui passo i parametri giusti...);
exit(0);
}
/*Il padre torna a ricevere richieste...*/
}

/*Routine per la gestione del segnale sigchld...*/
void deadHandler(int sig)
{
getimeofday(end,NULL);
long int execTime = (end->tv_sec - start->tv_sec) * 1000000 +
(end->tv_usec - start->tv_usec);
med = ALPHA * med + (1-ALPHA) * execTime;
}

Il mio problema e' questo:
il padre non sa a priori quando entrera' nella routine per
gestire il sigchld. Pertanto vorrei sapere questo: quando
entro nella deadHandler(...) la PRIMA volta, accedendo alla
varibile START, faccio riferimento alla sua PRIMA allocazione
(E quindi al suo primo valore)?! E quindi quando accedo alla
routine la SECONDA volta faccio riferimento alla SECONDA
allocazione della variabile oppure no ?!? E cosi' via...
In altre parole quando entro nella routine di gestione del
sigchld la PRIMA volta ed accedo a start, quest'ultima potrebbe essere stata sovrascritta perche' nel frattempo il
server ha ricevuto una (o piu') ulteriore richiesta e quindi
ha dato un nuovo valore a start ?! Oppure il fatto che
utilizzi la allocazione dinamica mi cautela da questo
"problema". (In pratica dovrebbe succedere qualcosa di simile a quello che accade nella ricorsione per fare in modo che gli
effetti siano quelli "desiderati"...)

Scusate se ho scritto una marea di roba e se, oltretutto...
sono stato pure poco chiaro... :confused:

thks

ilsensine
13-07-2004, 11:56
Hai una race grossa quanto una casa. Cosa succede se il server riceve una _seconda_ richiesta se ancora non ha ultimato la _prima_?

Ed_Bunker
13-07-2004, 12:28
Originariamente inviato da ilsensine
Hai una race grossa quanto una casa. Cosa succede se il server riceve una _seconda_ richiesta se ancora non ha ultimato la _prima_?

Intendi una corsa critica ?!? Solo il processo padre accede alla variabile start ed end e percio' non vedo come ci possano essere corse critiche... Il problema mi pare che sia, come ho detto, che quando il padre riceve il segnale SIGCHLD ed entra nella gestione della routine deadHandler(...) il valore di start potrebbe non essere quello "corrispondente" al processo figlio che ha inviato il SIGCHLD per comunicare la terminazione. Questo peche' successivamente il padre potrebbe aver ricevuto altre richieste sovrascrivendo la variabile start. Ma la malloc non dovrebbe fare qualcosa di "simile" a cio' che accade con la ricorsione ??!

ilsensine
13-07-2004, 12:37
Appunto, questa si chiama "race condition".
Malloc alloca solo memoria, poi come la gestisci sono affari tuoi.

Ed_Bunker
13-07-2004, 14:03
Originariamente inviato da ilsensine
Appunto, questa si chiama "race condition".
Malloc alloca solo memoria, poi come la gestisci sono affari tuoi.
Il fatto che la malloc allochi memoria ok, ci siamo. La race condition data dal fatto che, se il server riceve una ulteriore richiesta, dopo aver gia' lanciato un processo figlio, il puntatore "start" va a puntare ad una nuova area di memoria ? E percio' il valore impostato dalla gettimeofday(...) (La prima) viene "perso" ?!:confused:

Quello che vorrei avere e' un comportamento simile a quello che si ha utilizzando la ricorsione....

P.S.: dichiarando il puntatore "struct timeval start = (struct timeval*) malloc (sizeof(struct timeval))" all'interno del ciclo while quando entro nella deadHandler(...) il riferimento a 'start' sarebbe quello "atteso" ?!? Se si, pero', come faccio a fare in modo che da deadHandler(...) 'start' sia visibile visto che e' dichiarata all'interno del ciclo ?! :confused:

thks

ilsensine
13-07-2004, 14:27
Originariamente inviato da Ed_Bunker
Il fatto che la malloc allochi memoria ok, ci siamo. La race condition data dal fatto che, se il server riceve una ulteriore richiesta, dopo aver gia' lanciato un processo figlio, il puntatore "start" va a puntare ad una nuova area di memoria ? E percio' il valore impostato dalla gettimeofday(...) (La prima) viene "perso" ?!:confused:
Certo. In più hai un memory leak.

La soluzione non è banale, neanche usando delle liste dinamiche (non puoi usare strategie di lock con il signal handler!!). Meglio se usi un array statico, limitando il numero massimo di richieste che puoi soddisfare contemporaneamente.

Ed_Bunker
13-07-2004, 15:40
Originariamente inviato da ilsensine
Certo. In più hai un memory leak.

La soluzione non è banale, neanche usando delle liste dinamiche (non puoi usare strategie di lock con il signal handler!!). Meglio se usi un array statico, limitando il numero massimo di richieste che puoi soddisfare contemporaneamente.

Mmmm... inizialmente avevo provato proprio usando un array dinamico ma le operazioni su tale array facevano un po' perdere di efficienza al tutto... E stavo cercando qualcosa per rendere il tutto piu' "snello". Avevo pensato (Su suggerimento di un utente del forum...:D )di utilizzare il campo "sa_sigaction" anziche' "sa_handler" visto che il primo da maggiori informazioni riguardanti il figlio che ha lanciato il SIGCLHD. Ha come parametro anche un puntatore ad una struct (siginfo_t *) che contiene, tra l'altro, questi due campi:

clock_t si_utime; /*User time consumed*/
clock_t si_stime; /*System time consumed*/

Da questi due valori potrei risalire al tempo di esecuzione del processo figlio che ha lanciato il SIGCHLD ? Quale e' la differenza tra user time consumed e system time consumed ? Si riferiscono al tempo trascorso a livello utente e quello impiegato a livello kernel ? :)

ilsensine
13-07-2004, 15:43
Esattamente, ma non necessariamente la loro somma è il tempo totale trascorso: questo perché il tempo totale viene ripartito tra i vari task.

Per me devi usare un array statico di strutture; ogni struttura deve avere il pid del processo (oppure 0 se è libera), e il tempo di inizio. Nel sighandler cerchi nell'array la struttura con il pid uguale a quello del processo che ha generato il segnale; quando ha finito, mette questo valore a 0 per liberarla per altri task. Mi sembra semplice, no?

Ed_Bunker
13-07-2004, 16:38
Originariamente inviato da ilsensine
Esattamente, ma non necessariamente la loro somma è il tempo totale trascorso: questo perché il tempo totale viene ripartito tra i vari task.

Per me devi usare un array statico di strutture; ogni struttura deve avere il pid del processo (oppure 0 se è libera), e il tempo di inizio. Nel sighandler cerchi nell'array la struttura con il pid uguale a quello del processo che ha generato il segnale; quando ha finito, mette questo valore a 0 per liberarla per altri task. Mi sembra semplice, no?

Si ma questo implica comunque delle ricerche all'interno dell'array ogni volta che un processo figlio mi manda un SIGCHLD. Inoltre quando devo creare un nuovo processo (Quando faccio la fork() ) devo anche registrarlo all'interno dell'array andando a cercare la prima posizione libera. L'array, infatti, potrebbe avere dei "buchi" al proprio interno dovuti a processi che lanciati successivamente ad altri hanno terminato prima la loro esecuzione. Le prestazioni credo che ne risentano sensibilmente...

Tornando al discorso della sa_sigaction fare la somma tra lo user time consumed e il system time consumed non "restituisce" un risultato analogo a quello ottenuto facendo uso della gettimeofday() ?!?

ilsensine
13-07-2004, 16:50
Non credo affatto che le prestazioni vengano penalizzate, la ricerca dentro un array è molto veloce. Tieni conto inoltre che stai usando funzioni (tipo fork) che non sono veloci quanto una addizione ;)

Ed_Bunker
13-07-2004, 23:38
Originariamente inviato da ilsensine
Non credo affatto che le prestazioni vengano penalizzate, la ricerca dentro un array è molto veloce. Tieni conto inoltre che stai usando funzioni (tipo fork) che non sono veloci quanto una addizione ;)
Dipende dalla lunghezza dell'array... :D E ad ogni modo complica un po' le cose. Speravo proprio ci fosse il modo per risalire al tempo di esecuzione avendo a disposizione i campi: si_utime, si_stime...