PDA

View Full Version : [C] Programmazione di sistema: fork \ pipes


nalsk
02-07-2010, 18:03
Salve a tutti, è da un pò di tempo che sono appresso ad un progetto in cui sto incontrando alcuni problemi riguardanti la creazione e la "manutenzione" di più processi child nati dallo stesso parent.

La funzione ghostkeeper (parent) dovrebbe essere in grado di capire quando uno dei 4 ghost (childs) termina, e crearne a sua volta un'altro identico. In poche parole i processi child devono sempre rimanere 4. Ed è ciò che non riesco a fare.
Tutto ok quando partono i primi 4, ma quando uno dei 4 primi fa la exit cominciano i problemi: processi che si moltiplicano o che non nascono direttamente.
Forse mi spiego meglio con il codice:

intanto gli header che uso sono i seguenti:


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <curses.h>
#include <signal.h>
#include <sys/wait.h>


ed il problema lo riscontro in questa funzione:


void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

pid_t id[4]; // id[i] : il PID del fantasma con id "i"
int death_id = 0;

int i;

// handler dei segnali dei figli
signal (SIGCHLD, sigchld_h);

// faccio la fork per il fantasma e annoto il PID
for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) ghost(i);
id[i]=pid_ghost[i];
}
// ora so che tutti i fantasmi sono entrati e conosco il loro PID


// rimaniamo in lettura sulla pipe aspettando che un fantasma muoia
// (quando un fantasma muore scrive su questa pipe il suo ID

// ############ questo è ciò che non funziona #
// ####################################
while(1) {
close(pipe_ghost_death[1]);
read(pipe_ghost_death[0],&death_id,sizeof(death_id));

pid_ghost[death_id]=0;
if(pid_ghost[death_id]=fork() == 0) ghost(death_id);
id[death_id] = pid_ghost[death_id];
}
// fa un casino della malora
// guardare cosa succede nei processi sul monitor di sistema
// con il codice fantasma che va in exit dopo un tot di secondi
// ########################################
}


allego anche il codice della funzione ghost(id);

void ghost (int id) {
int x = 0;
// il processo una volta pronto per fare la exit dovrebbe comunicare
// tramite la pipe il suo id in modo che ghostkeeper crei un altro processo

while(x++ < 4+(2*id)) sleep(1);

// prima di andare in exit deve comunicare tramite questa pipe
// al ghostkeeper che il proprio ID (1 dei 4) sta per essere liberato

close(pipe_ghost_death[0]);
write(pipe_ghost_death[1],&id,sizeof(id));

// ed esce

exit(0);
}


chiedo scusa per il codice non proprio perfetto e l'inondazione di commenti, ma sono abituato a commentare ogni singola H.. :)

marco.r
02-07-2010, 18:35
vedo che hai installato un signal handler per SIGCHLD... non e' che magari ricrei il processo anche la' ?
cmq io la farei piu' semplice.
Ad esempio potresti, una volta creati i processi, usare una wait(2) per aspettare che uno dei figli muoia. La wait stessa ti ritorna il pid del processo morto e quindi puoi fare ripartire quello che ti interessa. mi sembra molto piu' semplice.

nalsk
02-07-2010, 18:59
ok grazie mille del consiglio, stasera appena avrò il progetto completo davanti vedrò di implementarlo con la wait :)

EDIT:
<<
il signal handler fa una waitpid di questo genere quà:


while ( (pid = waitpid(-1,&status,WNOHANG)) > 0)


per evitare gli zombie.
Sono possibili problemi derivati da questa implementazione?
>>

tuccio`
03-07-2010, 20:42
ok grazie mille del consiglio, stasera appena avrò il progetto completo davanti vedrò di implementarlo con la wait :)

EDIT:
<<
il signal handler fa una waitpid di questo genere quà:


while ( (pid = waitpid(-1,&status,WNOHANG)) > 0)


per evitare gli zombie.
Sono possibili problemi derivati da questa implementazione?
>>Ma con la pipe che ci fai? Insomma, se fai waitpid sai qual è il pid del processo terminato, quindi di conseguenza puoi ricavarne l'id. Non ti starai complicando la vita? Se devi solo tenere 4 processi attivi la cosa non è così complicata, basta che il sighandler ricavi l'id e avvii un altro processo

nalsk
04-07-2010, 01:29
Si hai ragione, in teoria dovrei sfruttare il meno possibile le pipes, ed è vero che mi sono complicato la vita. Sono alle prime armi con la programmazione di sistema utilizzando processi e pipes.. Però secondo me di logica dovrebbe essere buono! Cioè ok utilizzare wait e/o waitpid, però dal momento che il processo figlio scrive sulla pipe significa che l'istruzione successiva sarà per forza di cose una exit.. Quindi appena il read del padre può leggere dalla pipe è scontato che l'id letto si sia liberato e che debba essere riempito con un nuovo processo.. Mettendo il caso che il processo in exit non sia stato ancora terminato dopo aver scritto, il padre lo esclude lo stesso dalla lista degli id e poi si arrangerà il figlio a terminarsi in pace.. Non ci vedo l'errore ecco tutto..

tuccio`
04-07-2010, 10:43
Si hai ragione, in teoria dovrei sfruttare il meno possibile le pipes, ed è vero che mi sono complicato la vita. Sono alle prime armi con la programmazione di sistema utilizzando processi e pipes.. Però secondo me di logica dovrebbe essere buono! Cioè ok utilizzare wait e/o waitpid, però dal momento che il processo figlio scrive sulla pipe significa che l'istruzione successiva sarà per forza di cose una exit.. Quindi appena il read del padre può leggere dalla pipe è scontato che l'id letto si sia liberato e che debba essere riempito con un nuovo processo.. Mettendo il caso che il processo in exit non sia stato ancora terminato dopo aver scritto, il padre lo esclude lo stesso dalla lista degli id e poi si arrangerà il figlio a terminarsi in pace.. Non ci vedo l'errore ecco tutto..in realtà anche a livello di logica un errore c'è, perché tu non dovresti mai fare nessuna assunzione sul modo in cui i processi vengono schedulati, quindi il fatto che l'istruzione successiva sia una exit non vuol dire niente, perché un processo può essere prerilasciato in qualsiasi momento, quindi a rigore i tuoi processi potrebbero anche essere prerilasciati dopo aver scritto e magari dare il tempo di crearne altri, arrivando a creare, in qualche istante, più di 4 processi

marco.r
04-07-2010, 13:44
Si hai ragione, in teoria dovrei sfruttare il meno possibile le pipes, ed è vero che mi sono complicato la vita. Sono alle prime armi con la programmazione di sistema utilizzando processi e pipes.. Però secondo me di logica dovrebbe essere buono! Cioè ok utilizzare wait e/o waitpid, però dal momento che il processo figlio scrive sulla pipe significa che l'istruzione successiva sarà per forza di cose una exit.. Quindi appena il read del padre può leggere dalla pipe è scontato che l'id letto si sia liberato e che debba essere riempito con un nuovo processo.. Mettendo il caso che il processo in exit non sia stato ancora terminato dopo aver scritto, il padre lo esclude lo stesso dalla lista degli id e poi si arrangerà il figlio a terminarsi in pace.. Non ci vedo l'errore ecco tutto..

Fai lavoro in piu' per nulla, e potenzialmente sbagliato.
Sbagliato per le motivazioni portate da tuccio, inutile perche' ci sono gia' le funzioni del sistema per fare quello che serve a te.
Secondo me elimina le pipe, non ti servono a niente. Fatto questo hai due strade: la prima e' quella di installare un signal handler (come hai gia' fatto), li' verificare il pid del processo che e' finito e farlo ripartire. La seconda e' quella di fare la wait direttamente nella funzione principale (avendo cura di verificare che la wait e' stata attivata perche' e' morto il figlio non per altri motivi).

nalsk
04-07-2010, 13:54
Grazie mille per le delucidazioni ragazzi :)

nalsk
04-07-2010, 15:47
Oggi progetto alla mano, ho modificato secondo i vostri consigli in questo modo:

padre

void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

pid_t death_PID;
int status;

int i;

for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);
for(i=0;i<4;i++) {
if(pid_ghost[i] == death_PID) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}
}
sleep(1);
}
}


e figlio


void ghost (int id) {

int x = 0;

while(x++ < 4+(4*id)) sleep(1);
exit(0);
}


ho tolto l'handler e la pipe, adesso sto usando semplicemente una wait.
Il programma funziona discretamente se non fosse che la wait sicuramente non mi ritorna il pid, anche se il figlio termina in modo pulito con una exit(0).
Ho provato con entrambi i metodi, sia la wait che la waitpid che vedete commentata (che tra l'altro dovrebbero essere equivalenti usate in quel modo), ed in ogni caso non è vera la if(ghost_pid[i] == death_PID). Evidentemente mi viene restituito un errore o comunque qualcosa che non è l'id del figlio. Sicuramente voi esperti vi metterete a ridere :fagiano: ma non saprei che fare.. ho provato anche con diverse exit e nel gapil non trovo la risposta..

tuccio`
04-07-2010, 17:47
Oggi progetto alla mano, ho modificato secondo i vostri consigli in questo modo:

padre

void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

pid_t death_PID;
int status;

int i;

for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);
for(i=0;i<4;i++) {
if(pid_ghost[i] == death_PID) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}
}
sleep(1);
}
}


e figlio


void ghost (int id) {

int x = 0;

while(x++ < 4+(4*id)) sleep(1);
exit(0);
}


ho tolto l'handler e la pipe, adesso sto usando semplicemente una wait.
Il programma funziona discretamente se non fosse che la wait sicuramente non mi ritorna il pid, anche se il figlio termina in modo pulito con una exit(0).
Ho provato con entrambi i metodi, sia la wait che la waitpid che vedete commentata (che tra l'altro dovrebbero essere equivalenti usate in quel modo), ed in ogni caso non è vera la if(ghost_pid[i] == death_PID). Evidentemente mi viene restituito un errore o comunque qualcosa che non è l'id del figlio. Sicuramente voi esperti vi metterete a ridere :fagiano: ma non saprei che fare.. ho provato anche con diverse exit e nel gapil non trovo la risposta..il man è tuo amico

on success, returns the process ID of the terminated child; on error, -1 is returned.

dovresti sempre gestire gli errori, specialmente nelle slow system calls (ad esempio wait, read e write), ma anche altre, come la fork che può fallire, in questo caso:


do {
death_PID = wait(&status);
} while (death_PID < 0);
for ...

marco.r
04-07-2010, 19:40
Oggi progetto alla mano, ho modificato secondo i vostri consigli in questo modo:

padre

void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

pid_t death_PID;
int status;

int i;

for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);
for(i=0;i<4;i++) {
if(pid_ghost[i] == death_PID) {
if(pid_ghost[i]=fork() == 0) ghost(i);
}
}
sleep(1);
}
}


e figlio


void ghost (int id) {

int x = 0;

while(x++ < 4+(4*id)) sleep(1);
exit(0);
}


ho tolto l'handler e la pipe, adesso sto usando semplicemente una wait.
Il programma funziona discretamente se non fosse che la wait sicuramente non mi ritorna il pid, anche se il figlio termina in modo pulito con una exit(0).
Ho provato con entrambi i metodi, sia la wait che la waitpid che vedete commentata (che tra l'altro dovrebbero essere equivalenti usate in quel modo), ed in ogni caso non è vera la if(ghost_pid[i] == death_PID). Evidentemente mi viene restituito un errore o comunque qualcosa che non è l'id del figlio. Sicuramente voi esperti vi metterete a ridere :fagiano: ma non saprei che fare.. ho provato anche con diverse exit e nel gapil non trovo la risposta..

che valori ti torna wait ? Se ti torna -1 controlla errno.
In ogni caso fai attenzione a come controlli il risultato della fork: quando il risultato e' 0 (sei nel processo figlio), non solo devi eseguire ghost() (o quello che vuoi fargli eseguire) ma stare attento che finita la sua esecuzione questo non prosegua a fare quello che fa il padre. Immagino che all'interno di ghost() ci sia uan exit(), sarebbe comunque buona cosa cautelarsi facendo seguire alla ghost() un bell'exit esplicito. questo potrebbe pure spiegare tutti i vari spawn che avevi all'inizio:


for(i=0;i<4;i++)
{
if(pid_ghost[i] == death_PID)
{
if(pid_ghost[i]=fork() == 0)
{
ghost(i);
exit(0);
}
}
}

nalsk
05-07-2010, 13:14
Si marco avevo pensato ad un problema simile per gli spawn verificati in precedenza..però non essendosi più presentati ho lasciato perdere. Ora per sicurezza farò la exit esplicita :)

nalsk
05-07-2010, 13:29
Grazie tuccio, farò anche la gestione degli errori, ma non penso serva a molto..
Se io faccio barbaramente:


death_PID = wait(&status);
if(ghost_pid[i]=fork() == 0) ghost(i);


funziona tutto.. ovviamente l'ID è errato perchè l'array dei pid non viene modificato nella posizione giusta, ma almeno i processi sono sempre 4 :)

gestendo l'errore avrei sempre lo stesso risultato.. che non si entra nel while(death_PID > 0) ..
A me interesserebbe più che altro come fare in modo che la wait dia success e capire da dove ne esca l'errore che mi fa restituire -1 :rolleyes:

tuccio`
05-07-2010, 14:16
Grazie tuccio, farò anche la gestione degli errori, ma non penso serva a molto..
Se io faccio barbaramente:


death_PID = wait(&status);
if(ghost_pid[i]=fork() == 0) ghost(i);


funziona tutto.. ovviamente l'ID è errato perchè l'array dei pid non viene modificato nella posizione giusta, ma almeno i processi sono sempre 4 :)

gestendo l'errore avrei sempre lo stesso risultato.. che non si entra nel while(death_PID > 0) ..
A me interesserebbe più che altro come fare in modo che la wait dia success e capire da dove ne esca l'errore che mi fa restituire -1 :rolleyes:se wait restituisce -1 errno assume il valore corrispondente all'errore (ancora, queste cose sono scritte nel man)

nalsk
05-07-2010, 15:42
Ho aggiunto la gestione degli errori, ed alcune fprintf per vedere un pò ciò che viene o non viene eseguito.

Adesso però il problema sembra un'altro, legato strettamente alle fork.

questo è il codice del padre:

void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

file_error_output = fopen("/home/znd/Scrivania/LabOS1/pacman_pipes/errors.txt","w");
fprintf(file_error_output,"OK\n");

pid_t death_PID;
int status;

int i;

fprintf(file_error_output,"faccio i primi fantasmi\n");
for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) {
fprintf(file_error_output,"fatto %d\n",i);
ghost(i);
exit(0);
}
}
fprintf(file_error_output,"finito\n");

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);

if(death_PID > 0)
for(i=0;i<4;i++) {
fprintf(file_error_output,"Entro nella for %d\n",i);
if(pid_ghost[i] == death_PID) {
fprintf(file_error_output,"death_PID trovato!\n");
if(pid_ghost[i]=fork() == 0) {
fprintf(file_error_output,"partito il ghost\n");
ghost(i);
exit(0);
}
}
}
else {
*errore = strerror(errno);
fprintf(file_error_output,"Output wait: %d - Errore: %s %d\n",death_PID,errore,errno);
}
sleep(1);
}
}


e questo è l'output in errors.txt


OK
faccio i primi fantasmi
fatto 0
OK
faccio i primi fantasmi
fatto 1
OK
faccio i primi fantasmi
fatto 2
OK
faccio i primi fantasmi
fatto 3


Sembra che dopo la for iniziale tutto il codice seguente non venga eseguito..!
Vabbè cercherò una soluzione..evidentemente dovrò riguardarmi bene tutta la dinamica dei processi..

tuccio`
05-07-2010, 17:38
Ho aggiunto la gestione degli errori, ed alcune fprintf per vedere un pò ciò che viene o non viene eseguito.

Adesso però il problema sembra un'altro, legato strettamente alle fork.

questo è il codice del padre:

void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

file_error_output = fopen("/home/znd/Scrivania/LabOS1/pacman_pipes/errors.txt","w");
fprintf(file_error_output,"OK\n");

pid_t death_PID;
int status;

int i;

fprintf(file_error_output,"faccio i primi fantasmi\n");
for(i=0;i<4;i++) {
if(pid_ghost[i]=fork() == 0) {
fprintf(file_error_output,"fatto %d\n",i);
ghost(i);
exit(0);
}
}
fprintf(file_error_output,"finito\n");

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);

if(death_PID > 0)
for(i=0;i<4;i++) {
fprintf(file_error_output,"Entro nella for %d\n",i);
if(pid_ghost[i] == death_PID) {
fprintf(file_error_output,"death_PID trovato!\n");
if(pid_ghost[i]=fork() == 0) {
fprintf(file_error_output,"partito il ghost\n");
ghost(i);
exit(0);
}
}
}
else {
*errore = strerror(errno);
fprintf(file_error_output,"Output wait: %d - Errore: %s %d\n",death_PID,errore,errno);
}
sleep(1);
}
}


e questo è l'output in errors.txt


OK
faccio i primi fantasmi
fatto 0
OK
faccio i primi fantasmi
fatto 1
OK
faccio i primi fantasmi
fatto 2
OK
faccio i primi fantasmi
fatto 3


Sembra che dopo la for iniziale tutto il codice seguente non venga eseguito..!
Vabbè cercherò una soluzione..evidentemente dovrò riguardarmi bene tutta la dinamica dei processi..prima di tutto ti consiglio di imparare ad utilizzare gdb :D

poi, aggiungo, mi pare impossibile che non venga eseguita almeno questa riga:

fprintf(file_error_output,"finito\n");

magari potresti assicurarti che la fprintf scriva aggiungendo una bella fflush(file_error_output) appena dopo quest'istruzione

nalsk
05-07-2010, 19:42
Ho risolto il problema! Erano semplicemente da gestire meglio le fork. Ho solo reso le chiamate più esplicite di quanto lo erano prima.

Ringrazio tantissimo chi mi ha aiutato, soprattutto tuccio :D

Per correttezza posto il codice funzionante:


void ghostkeeper (void) {

my_pid = getpid();
close(pipe_pids[0]);
write(pipe_pids[1],&my_pid,sizeof(my_pid));

file_error_output = fopen("errors.txt","w");


int i;

for(i=0;i<4;i++) {
pid_ghost[i]=fork();
if(pid_ghost[i] == 0) {
ghost(i);
exit(0);
}
else;
}

while(1) {

// death_PID = waitpid(WAIT_ANY,&status,0);
death_PID = wait(&status);

if(death_PID > 0)
for(i=0;i<4;i++) {
if(pid_ghost[i] == death_PID) {
pid_ghost[i]=fork();
if(pid_ghost[i] == 0) {
ghost(i);
exit(0);
} else;
}
}
else {
*errore = strerror(errno);
fprintf(file_error_output,"Output wait: %d - Errore: %s %d\n",death_PID,errore,errno);
}
sleep(10);
}
}