PDA

View Full Version : [C] Segnale SIGXCPU e somma dei tempi di cpu tra processo padre e figli.


fracarro
26-03-2008, 20:06
Salve ragazzi. Avrei bisogno di una consulenza per un problemino che mi sta dando filo da torcere.
Consideriamo il codice qui sotto:


int main(){

struct rlimit limitbuf;
int i;

getrlimit(RLIMIT_CPU, &limitbuf);
limitbuf.rlim_cur = 3602;
limitbuf.rlim_max = 3700;
setrlimit(RLIMIT_CPU, &limitbuf);

if( signal(SIGXCPU,GestoreKill)==SIG_ERR ){
printf("errore di ritorno della signal (SIGXCPU).\n");
exit(0);
}

for(i=1;1<k;i++)
system("gambit <input_file | head-1 >output_file");

................
................

return 1;

}

In pratica dopo 3602 secondi se il programma sta ancora girando viene generato il segnale di SIGXCPU che la funzione GestoreKill cattura e fa delle cose che non ci interessano. Il problema è questo. Io vorrei che a partire dalla prima istruzione del main cominci il conteggio di questi 3602 secondi e che in questo conteggio venga considerato anche il tempo che trascorre durante la chiamata al gambit. Invece il segnale viene lanciato dopo che il tempo di CPU utilizzato dal programma "escluso il tempo di cpu utilizzato dal programma esterno invocato tramite la system" raggiunge i 3602 secondi. Ovviamente questo è frustrante perchè se per esempio la system richiede 30 minuti il mio programma verrà ammazzato dopo 3602+30*k minuti. C'è un modo per far si che il segnale venga inviato dopo 3602 secondi di utilizzo di CPU da parte del programma e dei programmi esterni da esso invocati?


P.S. Ovviamente usare un alarm non va bene perchè quella conterebbe il tempo di sistema e non quello di cpu.

P.P.S. Un'alternativa alla system potrebbe essere quella di usare una fork per creare un processo figlio e tramite una execl inviare il comando shell ma non so se in questo modo il tempo di cpu usato dal padre e dal figlio venga sommato e soprattutto ho avuto vari problemi nell'utilizzare la execl per lanciare il comando shell. Idee?

ilsensine
26-03-2008, 23:08
L'utilizzo di RLIMIT_CPU è coerente con quello che deve fare, purtroppo. Non oso chiedere quale masochistico requisito ti impone questa misura.

La soluzione non è semplice, ma ci possiamo inventare qualcosa. Intanto ti consiglio di cambiare la system con l'implementazione esplicita, fork() + exec() + waitpid(). Ci consente un pò di flessibilità in più, ad esempio ci consente di conoscere il pid del child.

In un primo modo di procedere, devi conoscere dal processo parent quante risorse usa il child; possiamo procedere come top, che usa i campi utime e stime di /proc/<pid>/stat (v. man 5 proc). Il problema è che questi campi sono in jiffies, secondo la documentazione, e non c'è un modo per capire a quanto vale un jiffie (un tempo era fisso a 1/100 di secondo, oggi può variare).

Un modo più raffinato è quello usato da time. Ovvero non attendere la chiusura del child tramite wait/waitpid, ma usa la wait4 (v. man wait4). Ti fornirà i tempi esatti occupati dal child, che puoi scalare dal totale (il tempo del processo corrente lo ottieni con getrusage).

Ovviamente in entrambe le soluzioni devi inventarti qualcosa se vuoi beccare il termine dell'utilizzo del tempo anche mentre un child è in esecuzione.

Rimane da provare getrusage(RUSAGE_CHILDREN), forse somma da solo i tempi di tutti i child.

fracarro
27-03-2008, 11:20
La soluzione non è semplice, ma ci possiamo inventare qualcosa. Intanto ti consiglio di cambiare la system con l'implementazione esplicita, fork() + exec() + waitpid(). Ci consente un pò di flessibilità in più, ad esempio ci consente di conoscere il pid del child.


Ok come si fa? Mi spiego meglio, consideriamo il codice così modificato:

int main(){

int status;
double tempo_tot_figli=0, start,tempo_corrente_padre;
struct rusage risorse;

start = timer();

for(i=1;i<k;i++){
if(fork()!= 0){ // sono il padre e aspetto...
cout << "sono il padre prima wait" << endl;
wait3(&status,0,&risorse);
}
else{
//system("/usr/bin/gambit-lcp < gambit_vrp.nfg | head -1 > output_gambit_vrp.txt");
execlp("/usr/bin/gambit-lcp","gambit-lcp","<","gambit_vrp.nfg"," |","head","-1",">","output_gambit_vrp.txt",0);
}

tempo_tot_figli += risorse.ru_utime;
tempo_corrente_padre = start - timer();
if(tempo_corrente_padre + tempo_tot_figli > 3602)
exit(0);

// il padre legge dal file di output generato dal figlio...
..................
.................
}

}


- Il primo problema che devo risolvere riguarda la execlp. Non riesco in nessun modo a farla funzionare. Il gambit viene lanciato ma mi da errore sul file di input perchè probabilmente non gli piace il simbolo di redirezione "<". Ovviamente lo stesso identico comando lanciato tramite la system sullo stesso file di input funziona correttamente. Commetto qualche errore nell'utilizzo della execlp?

- Secondo. Come da te consigliato qui:

Un modo più raffinato è quello usato da time. Ovvero non attendere la chiusura del child tramite wait/waitpid, ma usa la wait4 (v. man wait4). Ti fornirà i tempi esatti occupati dal child, che puoi scalare dal totale (il tempo del processo corrente lo ottieni con getrusage).

indico al processo padre di aspettare il figlio tramite la wait3 che dovrebbe memorizzare anche le info riguardo il tempo di cpu utilizzato da quest'ultimo. Uso la wait3 invece che wait4 perchè nel mio condice il padre genera un figlio alla volta. E non possono andare in run due figli contemporaneamente.


Ovviamente in entrambe le soluzioni devi inventarti qualcosa se vuoi beccare il termine dell'utilizzo del tempo anche mentre un child è in esecuzione.


Per ora lascio perdere questo punto ancora più complicato. Diciamo che mi accontento di calcolare il tempo del figlio quando termina, sommarlo a quelli precedenti e al tempo del padre e se supera la soglia blocco il processo (come indicato dal codice dopo l'else, la timer è una funzione che ho scritto e che calcola il tempo di cpu del processo). Cosa ne pensi?

ilsensine
27-03-2008, 11:35
Ok come si fa? Mi spiego meglio, consideriamo il codice così modificato:
Ora che è giorno e mi sono svegliato bene, noto che il tuo problema è ancora un pò più complicato, in quanto non invochi un solo programma ma due.
Implementare bene la cosa non è banale, in quanto prima della exec devi aprire il file di input su stdin (per emulare < gambit_vrp.nfg); il pipe su head è una complicazione ulteriore, devi fare un ulteriore fork e connettere lo stdout di gambit con lo stdin di head, e lo stdout di head sul file di uscita.

Si può fare tutto, ma a questo punto mi chiedo se lasciare la semplice system() seguita da getrusage(RUSAGE_CHILDREN) possa risolvere in maniera semplice. Dovrebbe funzionare, ma fai una prova.