PDA

View Full Version : [C] Processi concorrenti su Linux: la waitpid... non aspetta!


Gica78R
29-04-2005, 18:47
Ciao!

Ho scritto un programmino che forka alcune volte creando un paio di processi figli, e uno di questi figli forka a sua volta per creare un... nipote :)
Tutti i processi devono semplicemente scrivere delle righe su un file aperto dal padre e, prima di terminare, ciascun processo deve attendere che i rispettivi figli abbiano terminato. Per questo utilizzo la funzione int waitpid(pid_t pid, int *status, int options), ma nel caso del processo che genera il 'nipote' a quanto pare questa funzione non si blocca in attesa che il nipote termini (lo si vede dall' output...)
Secondo voi da cosa puo' dipendere?

Nel caso la mia esposizione del problema fosse poco chiara (parlo un po' criptato... :p ), vi posto il listato...
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>

#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/types.h>
#include <sys/wait.h>


#define NUMVOLTE 40000
#define SEMKEY 500
#define LOOP 5000

int main()
{

union semun
{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};

/*Pongo stdout in modalita' UNBUFFERED */
setvbuf(stdout,NULL,_IONBF,0);

/* Dichiarazioni ed inizializzazioni delle strutture relative al file di output */

FILE *f;
char *nomefile="/provafiles.txt";
char nome[PATH_MAX];
memset((void*)nome,0,PATH_MAX);
strncpy(nome,getenv("PWD"),PATH_MAX);
if(strlen(nome)+strlen(nomefile)>PATH_MAX)
{
printf("Il path generato e' troppo lungo; il programma verra' terminato!\n");
exit(EXIT_FAILURE);
}
strncat(nome,nomefile,NAME_MAX);

/* Dichiarazione strutture relative al semaforo */

int semid;
const union semun semunion={1};
struct sembuf semw={0,-1,SEM_UNDO};
struct sembuf sems={0,1,SEM_UNDO};

/* Creazione ed inizializzazione semaforo */

semid=semget(SEMKEY,1,IPC_CREAT|IPC_EXCL|666);
if(semid<0)
{
perror("Errore semaforo");
exit(EXIT_FAILURE);
}

if(semctl(semid,0,SETVAL,semunion)<0)
{
perror("Errore init semaforo");
semctl(semid,0,IPC_RMID);
exit(EXIT_FAILURE);
}

/* Apertura in scrittura esclusiva del file per l'output;
se il file esiste gia', il programma termina /*

if ((f=fopen(nome,"wx"))==NULL)
{
perror("Errore nell'apertura del file");
exit(EXIT_FAILURE);
}

/* Pongo il file in modalita' UNBUFFERED */
setvbuf(f,NULL,_IONBF,0);

pid_t pid,pid2;

if ((pid=fork())==0)
{
/* PRIMO FIGLIO */
setvbuf(f,NULL,_IONBF,0);
if((pid2=fork())==0)
{
/* NIPOTE */
setvbuf(f,NULL,_IONBF,0);
int i,j;
for(i=0;i<NUMVOLTE;i++)
{
if((i+1)%100==0)
{
semop(semid,&semw,1); //WAIT sul semaforo
fprintf(f,"E' il nipote che scrive... %d\n",i+1);
semop(semid,&sems,1); //SIGNAL sul semaforo
for(j=0;j<LOOP;j++); // Ciclo vuoto per ritardare il nipote
}
}
return EXIT_SUCCESS;
} // FINE NIPOTE
/* PRIMO FILGLIO */
int i;
for(i=0;i<NUMVOLTE;i++)
if ((i+1)%100==0)
{
semop(semid,&semw,1); //WAIT semaforo
fprintf(f,"E' il primo figlio che scrive... %d\n",i+1);
semop(semid,&sems,1); //SIGNAL semaforo
}
int status;
waitpid(pid2,&status,1); //Attende la fine di NIPOTE
/* Quando NIPOTE termina, lo scrive sul file */
semop(semid,&semw,1);
fprintf(f,"Sono il primo figlio, il nipote e' terminato con lo stato %d\n",status);
semop(semid,&sems,1);
return EXIT_SUCCESS;
} // FINE PRIMO FIGLIO
else
{
/* PADRE */
if((pid2=fork())==0) //Crea un secondo figlio
{
/* SECONDO FIGLIO
int i;
for(i=0;i<NUMVOLTE;i++)
if((i+1)%100==0)
{
semop(semid,&semw,1);
fprintf(f,"E' il secondo figlio che scrive... %d\n",i+1);
semop(semid,&sems,1);
}
return EXIT_SUCCESS;
} // FINE SECONDO FIGLIO
int i;
for(i=0;i<NUMVOLTE;i++)
if ((i+1)%100==0)
{
semop(semid,&semw,1);
fprintf(f,"E' il padre che scrive... %d\n",i+1);
semop(semid,&sems,1);
}
}
//int status;
waitpid(pid,NULL,1); // Attende la fine del PRIMO FIGLIO
waitpid(pid2,NULL,1); // Attende la fine del SECONDO FIGLIO
fclose(f);
semctl(semid,0,IPC_RMID); //Rimuove il semaforo
printf("Programma terminato con successo...\n");
return 0;
}

Non commentate sull'utilita' del programma :D, e' solo per capire come usare certe funzioni e per avere un'idea di come funziona lo scheduler...
Per ciascuna scrittura sul file, uso un semaforo per accedervi in modo esclusivo; inoltre ho fatto in modo che l'accesso al file non sia bufferizzato.

Grazie per ogni suggerimento...
Gica

30/4/05, aggiornamento: ho aggiunto qualche commento al codice... :)

VegetaSSJ5
29-04-2005, 21:52
visto che ogni processo deve aspettare la terminazione di uno o più figli e non di processi "estranei" puoi usare la funzione wait() e non waitpid()

Gica78R
29-04-2005, 22:19
visto che ogni processo deve aspettare la terminazione di uno o più figli e non di processi "estranei" puoi usare la funzione wait() e non waitpid()
Lo so... era quello che avevo fatto all'inizio, ma poiche' l'esito era lo stesso ho provato con la waitpid(). :(

Ed_Bunker
30-04-2005, 01:23
Memorizzare lo stato di uscita dei figli che terminano (Anziche' mettere status a null) potrebbe servirti a capire meglio...

Gica78R
30-04-2005, 09:27
Memorizzare lo stato di uscita dei figli che terminano (Anziche' mettere status a null) potrebbe servirti a capire meglio...
Ho provato: il 'primo figlio' fa l'ultima scrittura (quella a 40000) e, nonostante il 'nipote' non sia ancora terminato, effettua comunque la fprintf posta dopo la waitpid. Il valore di status relativo al 'nipote' e' zero, cioe' il nipote termina con successo, ma in realta' esso non e' ancora terminato! :confused:

Ad ogni modo, il 'nipote' riesce a completare tutto il ciclo 'for' con le scritture...
Sara' mica una specie di ottimizzazione nell'accesso al file?


Gica

pela
30-04-2005, 11:52
mi sa che non si blocca perché gli dici di non bloccarsi, guarda infatti la chiamata
waitpid(pid2,&status,1); //Attende la fine di NIPOTE il terzo parametro (options) se impostato a WNOHANG (che è proprio uguale a 1) dice alla waitpid di non bloccarsi se il figlio è ancora in esecuzione (vedi man waitpid)
prova a metterci 0

Gica78R
30-04-2005, 12:10
mi sa che non si blocca perché gli dici di non bloccarsi, guarda infatti la chiamata
waitpid(pid2,&status,1); //Attende la fine di NIPOTE il terzo parametro (options) se impostato a WNOHANG (che è proprio uguale a 1) dice alla waitpid di non bloccarsi se il figlio è ancora in esecuzione (vedi man waitpid)
prova a metterci 0
Cacchio :eek: Ho confuso il significato di pid con quello di options nella chiamata a waitpid :doh:
Da 'man waitpid':The value of pid can be one of:

< -1 which means to wait for any child process whose process group ID
is equal to the absolute value of pid.

-1 which means to wait for any child process; this is the same
behaviour which wait exhibits.

0 which means to wait for any child process whose process group ID
is equal to that of the calling process.

> 0 which means to wait for the child whose process ID is equal to
the value of pid.
quindi pensavo di far bene mettendo un valore positivo in 'options' :stordita:

Grazie grazie mille :ave:, e' una cosa che non avrei mai ricontrollato :muro:

Gica