PDA

View Full Version : Problemino in C


boysna
24-05-2005, 15:47
Ciao ragazzi ho un problema con un programma fatto in C il cui codice è il seguente:

int glob = 10;
char buf[] = "Scritta da stdout\n";

int main(void) {
int var = 100;
pid_t pid;
write(STDOUT_FILENO, buf, sizeof(buf)-1);
printf("Prima della fork\n");
if(( pid = fork() ) == 0 ) { glob ++; var ++;}
else sleep(2);
printf("pid = %d, glob = %d, var = %d\n, getpid(), glob, var);
exit(0); }

Lanciando il programma dalla shell mi stampo come giustamente avevo previsto la scritta:

Scritta da stdout
Prima della fork
pid = 'pid del figlio', glob = 11, var = 101
pid = 'pid del padre', glob = 10, var = 100

Fin qua tutto ok se non fosse per il fatto che ho provato a lanciare il comando reindizzando l'output in un file dando il seguente comando alla shell:

esempio > temp.txt

esempio è il programma e temp un file di testo.

Ho visto nel file cosa è stato scritto, pensando che ci fosse scritto quello che precedentemente era stato stampato a video, e invece mi scrive nel file:

Scritta da stdout
Prima della fork
pid = 'pid del figlio', glob = 11, var = 101
Prima della fork
pid = 'pid del padre', glob = 10, var = 100

Qualcuno gentilmente mi spiega perchè???
Grazie a tutti!!!

71104
24-05-2005, 17:33
sei assolutamente sicuro che lo stesso codice del programma scriva cose diverse a video e sul file? perché mi sembra strano, non dovrebbe succedere... che succede se anziché sleep(2) ci metti sleep(500)?

Fenomeno85
24-05-2005, 17:38
non capisco perchè ti riscrive quel prima della fork dato che padre e figlio in quel punto non ci possono tornare :mbe:
~§~ Sempre E Solo Lei ~§~

Fenomeno85
24-05-2005, 17:38
aspetta che accendo la macchina virtuale che provo

~§~ Sempre E Solo Lei ~§~

Fenomeno85
24-05-2005, 17:52
bella lì lo fa anche a me :wtf:

~§~ Sempre E Solo Lei ~§~

Fenomeno85
24-05-2005, 17:56
allora io ho riscritto così :

#include <stdio.h>
#include <sys/types.h>

int glob = 10;
char buf [] = "scritta da stdout\n";

int main (){
int var = 100;
int pid;
printf ("\nPrima della fork\n");
pid = fork ();
if (pid == 0){
glob ++;
var ++;
printf ("Figlio\n");
}
else{
wait ();
printf ("Padre\n");
}
printf ("Pid = %d, glob= %d, var = %d\n", getpid(), glob, var);
exit (0);
}


~§~ Sempre E Solo Lei ~§~

mjordan
24-05-2005, 18:06
Quello sarebbe C? :p
Il motivo e' che durante la fork() vengono copiati anche i buffer destinati all'uso di stdout, e quindi quando il processo termina, viene effettuato un flush di tali buffer, facendo scrivere cose inaspettate (probabilmente perche' va a scrivere di nuovo un buffer per cui non si e' avuto ancora il tempo di svuotarlo). Pertanto e' opportuno svuotarlo da subito con fflush(), prima di forkare un processo. Inoltre sleep() non e' un metodo efficace di attendere un processo, devi usare la funzione wait().

Metticeli gli header nei programmi e soprattutto usa il tag code quando posti :D


/* -*-linux-c-*- */
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

int glob = 10;
char buf[] = "Scritta da stdout\n";

int main(void)
{
int var = 100;
pid_t pid;
int status;

write(STDOUT_FILENO, buf, sizeof(buf)-1);
printf("Prima della fork\n");
fflush(stdout);

if (!(pid = fork())) {
status = 0;
glob ++; var ++;
}
else
wait(&status);

printf("pid = %d, glob = %d, var = %d\n", getpid(), glob, var);

return status;
}

Fenomeno85
24-05-2005, 18:10
Quello sarebbe C? :p
Il motivo e' che durante la fork() vengono copiati anche i buffer destinati all'uso di stdout, e quindi quando il processo termina, viene effettuato un flush di tali buffer, facendo scrivere cose inaspettate (probabilmente perche' va a scrivere di nuovo un buffer per cui non si e' avuto ancora il tempo di svuotarlo). Pertanto e' opportuno svuotarlo da subito con fflush(), prima di forkare un processo. Inoltre sleep() non e' un metodo efficace di attendere un processo, devi usare la funzione wait().

Metticeli gli header nei programmi e soprattutto usa il tag code quando posti :D


hai qualcosa contro il mio codice?? :D
Cmq si svuotando il buffer funziona :D

~§~ Sempre E Solo Lei ~§~

mjordan
24-05-2005, 19:11
hai qualcosa contro il mio codice?? :D
Cmq si svuotando il buffer funziona :D

~§~ Sempre E Solo Lei ~§~

No pardon non mi stavo riferendo a te :p ma al codice originale :sofico:

71104
24-05-2005, 20:32
No pardon non mi stavo riferendo a te :p ma al codice originale :sofico: perché che ha di male? è pressoché illeggibile ma è sempre C! :)

mjordan
24-05-2005, 20:45
perché che ha di male? è pressoché illeggibile ma è sempre C! :)

No no su questo non ho dubbi solo che avere una exit() per terminare la main() non l'ho mai visto, inotre ha piu' warning che righe di codice dovute all'uso implicito delle funzioni :D

mjordan
24-05-2005, 20:47
Scusate il paragone che magari e' macabro ma mettere la exit() in quel punto e' come sparare a un morto :rotfl:

Non ho resistito :sofico:

VegetaSSJ5
24-05-2005, 21:54
Scusate il paragone che magari e' macabro ma mettere la exit() in quel punto e' come sparare a un morto :rotfl:

Non ho resistito :sofico:
:stordita:

71104
24-05-2005, 22:09
Scusate il paragone che magari e' macabro ma mettere la exit() in quel punto e' come sparare a un morto :rotfl:

Non ho resistito :sofico:
LOL :D è carino :D

Fenomeno85
25-05-2005, 06:18
exit e return si comportano allo stesso modo ;)

~§~ Sempre E Solo Lei ~§~

mjordan
25-05-2005, 06:54
exit e return si comportano allo stesso modo ;)

~§~ Sempre E Solo Lei ~§~

Fra exit() e return() c'e' invece una differenza enorme.
return() non fa altro che far proseguire l'esecuzione al punto precedente nello stack di chiamate (e quindi serve per uscire dalle funzioni con un valore di ritorno.
La funzione exit() invece chiama la funzione _Exit() che esegue le seguenti cose:

1) Tutti i file descriptor del processo vengono automaticamente chiusi.
2) L'exit status di un processo viene salvato e riportato come codice di uscita da un'eventuale funzione wait() o waitpid()
3) Tutti i processi figli di un processo terminato con exit() vengono cambiati di processo padre (sui sistemi unix vengono assegnati a init con PID = 1)
4) Viene emesso un SIGCHLD al processo genitore.
5) Se il processo controlla una sessione di terminale, viene emesso un SIGHUP a tutti i processi in foreground e il terminale viene disassociato dalla sessione di controllo.
6) Se la terminazione di un processo causa lo stato "orphaned" di un gruppo di processi, ogni membro di quel gruppo viene interrotto e viene emesso un SIGHUP. Inoltre viene emesso un SIGCONT a tutti i membri di quel gruppo.

Come vedi sono due cose estremamente diverse.

boysna
25-05-2005, 16:46
Quello sarebbe C? :p
Il motivo e' che durante la fork() vengono copiati anche i buffer destinati all'uso di stdout, e quindi quando il processo termina, viene effettuato un flush di tali buffer, facendo scrivere cose inaspettate (probabilmente perche' va a scrivere di nuovo un buffer per cui non si e' avuto ancora il tempo di svuotarlo).


Ma il buffer non viene svuotato quando viene effettuata la scritta a video?
E poi perchè succede solo quando ridireziono nel file e non quando stampo normalmente a video?

boysna
25-05-2005, 16:48
Fenomeno85 chi è la ragazza del tuo avatar?

71104
25-05-2005, 23:20
Fenomeno85 chi è la ragazza del tuo avatar?
ma lasciamo perdere, va' :oink: non tiriamo fuori un'altra volta quell'avatar magnetizzante... (magnetizza gli occhi)

Fenomeno85
26-05-2005, 07:20
ma lasciamo perdere, va' :oink: non tiriamo fuori un'altra volta quell'avatar magnetizzante... (magnetizza gli occhi)

:asd: non mi ricordo il nome in piazzetta c'è :D

~§~ Sempre E Solo Lei ~§~

Gica78R
26-05-2005, 10:14
Fra exit() e return() c'e' invece una differenza enorme.
return() non fa altro che far proseguire l'esecuzione al punto precedente nello stack di chiamate (e quindi serve per uscire dalle funzioni con un valore di ritorno.
La funzione exit() invece chiama la funzione _Exit() che esegue le seguenti cose:

1) Tutti i file descriptor del processo vengono automaticamente chiusi.
2) L'exit status di un processo viene salvato e riportato come codice di uscita da un'eventuale funzione wait() o waitpid()
3) Tutti i processi figli di un processo terminato con exit() vengono cambiati di processo padre (sui sistemi unix vengono assegnati a init con PID = 1)
4) Viene emesso un SIGCHLD al processo genitore.
5) Se il processo controlla una sessione di terminale, viene emesso un SIGHUP a tutti i processi in foreground e il terminale viene disassociato dalla sessione di controllo.
6) Se la terminazione di un processo causa lo stato "orphaned" di un gruppo di processi, ogni membro di quel gruppo viene interrotto e viene emesso un SIGHUP. Inoltre viene emesso un SIGCONT a tutti i membri di quel gruppo.

Come vedi sono due cose estremamente diverse.
Io ho qualche dubbio... Sempre con riferimento a un sistema Unix, leggendo GaPiL (http://gapil.firenze.linux.it/), paragrafi 2.1.2 e 3.2.4:
Normalmente un programma finisce quando la funzione main ritorna; una modalita' equivalente di chiudere il programma e' quella di chiamare direttamente la funzione exit (che viene comunque chiamata quando main ritorna).
Una forma alternativa e' quella di chiamare direttamente la system call _exit, che restituisce il controllo direttamente alla routine di conclusione dei processi del kernel.
[...]
Il valore di ritorno della funzione main, o quello usato nelle chiamate ad exit o _exit, viene chiamato stato di uscita e passato al processo che aveva lanciato il programma.


[...] le tre modalita' con cui un programma viene terminato in maniera normale: la chiamata di exit (che esegue le funzioni registrate per l'uscita e chiude gli stream), il ritorno della funzione main (equivalente alla chiamata di exit), e la chiamata ad _exit (che passa direttamente alle operazioni di terminazione del processo da parte del kernel).
Ora lo so che GaPiL non e' la Bibbia, pero' ad occhio mi sembra che, per terminare un processo, return ed exit sono equivalenti, anche se la seconda soluzione e' meno simpatica :D

Gica :cool:

mjordan
26-05-2005, 11:34
Io ho qualche dubbio... Sempre con riferimento a un sistema Unix, leggendo GaPiL (http://gapil.firenze.linux.it/), paragrafi 2.1.2 e 3.2.4:



Ora lo so che GaPiL non e' la Bibbia, pero' ad occhio mi sembra che, per terminare un processo, return ed exit sono equivalenti, anche se la seconda soluzione e' meno simpatica :D

Gica :cool:

Si parla di conclusione "per ritorno al processo chiamante".
Se si parla di un processo generato da una fork(), fare un return() o una exit() significa comunque spostare l'esecuzione al programma chiamante. Quindi in un certo senso sono equivalenti come effetto, ma non come funzioni (la differenze che elencavo io si riferivano al comportamento interno). Inoltre un processo non va mai terminato con exit(), ma sarebbe opportuno terminarlo con _exit(). La differenza risiede che un processo riceve tutti i buffer copiati dalla fork(), con _exit() eviti di svuotarli cosi' che possano essere stampati dal processo chiamante (e svuotati dal processo chiamante).

Ritornare da una funzione main() o interrompere una funzione main() ha come effetto quello di spostare l'esecuzione alla shell da dove si e' avviato il programma (cioe' terminarlo) ecco perche' come effetto e' sostanzialmente equivalente. Se invece usi exit() al posto di return() in una qualsiasi funzione chiamata nel programma, ti accorgi che il funzionamento e' differente.
Per esempio:



#include <stdio.h>

int func(void)
{
printf("Sono nella funzione %s\n", __func__);
exit(1);
}

int main(void)
{
func();
printf("Sono nella main\n");

return 0;
}


Una volta raggiunta la exit() nella funzione func(), la main() non avra' nessun modo di richiamare la printf(), perche' la exit() non torna alla funzione chiamante come avrebbe fatto una return() ma termina proprio l'esecuzione del programma.

Mi sembra talmente ovvio il concetto che mi viene da pensare che non ho capito io quello che si vuole dire.

Gica78R
26-05-2005, 11:55
Non per insistere, ma l'istruzione di ritorno di un processo (non di una funzione), non richiama semplicemente la funzione exit?
Le operazioni che hai elencato (chiusura dei file descriptor, memorizzazione dello stato di terminazione, assegnazione del nuovo padre ai processi "orfani", invio del segnale SIGCHLD, ecc...) vengono eseguite comunque, qualunque sia la modalita' di terminazione tra return, exit e _exit. La differenza dovrebbe esserci solo tra exit ed _exit, ma non riguarda le operazioni citate, che sono comuni ad entrambe...
Io faccio sempre riferimento a GaPiL, quindi potrei anche sbagliare oppure interpretare male quello che leggo...
La differenza che mostri con quell' esempio di codice, invece, e' chiara, ma non e' quello a cui mi riferivo.

Ciao,
Gica

mjordan
26-05-2005, 15:10
La differenza che mostri con quell' esempio di codice, invece, e' chiara, ma non e' quello a cui mi riferivo.

Ciao,
Gica

Bene, spiegami a cosa ti riferisci esattamente perche' allora non ho capito. :)

Gica78R
26-05-2005, 15:32
Bene, spiegami a cosa ti riferisci esattamente perche' allora non ho capito. :)
Mi riferisco alla conclusione di un processo: da quanto ne so, il ritorno dalla funzione main (tramite return) e' equivalente ad invocare direttamente la funzione exit(), quindi fare int main()
{
printf("Ciao\n");
return 0;
} e' identico aint main()
{
printf("Ciao\n");
exit(0);
} anche per quanto riguarda le operazioni eseguite poi dal kernel per terminare il processo...

Siamo d'accordo? :)

mjordan
26-05-2005, 16:05
Su questo siamo d'accordo. Ma questo e' un caso particolare. Cioe' che ritornare dalla funzione main() sia uguale a uscire da un programma. Ed ecco perche' prima ho fatto la battuta "E' come sparare a un morto". Cioe' se uno sta morendo, di consumarci un proiettile non ne vale la pena :rotfl:

Ma proprio perche' questo e' un caso particolare, non basta per affermare "exit() e' equivalente a return()", che invece sono due funzioni distinte con effetti differenti.

In genere, la convenzione e' quella che le funzioni debbano ritornare e i programmi debbano terminare seguendo il loro naturale flusso di esecuzione. La funzione exit() ha motivo di esistere per quelle circostanze in cui il programma sa' a priori di non poter continuare correttamente, perche' manca qualcosa. Anche se il programma sta terminando (e quindi effettuera' tutte le operazioni che esegue la exit()) resta comunque un modo stilistico dubbio di procedere. Questo caso particolare della exit(), per esempio, puo' essere esteso alle normali funzioni con return():


void func(char * msg)
{
printf("Il mio messaggio e': %s\n", msg);

return;
}


Se la funzione e' void, e' inutile mettere una return, tanto la funzione uscira' lo stesso senza codice di uscita. Cosi, se la main() sta terminando, e' inutile mettere una exit(), mentre e' necessario usare una return() semplicemente perche' e' necessario un codice di uscita. La distinzione e' molto sottile ma evidente.

sirus
26-05-2005, 17:23
Scusate il paragone che magari e' macabro ma mettere la exit() in quel punto e' come sparare a un morto :rotfl:

Non ho resistito :sofico:
:rotfl:
d'accordo con le differenze tra exit() e return() ;) anche se con la "funzione main" le differenze non sono tangibili, se si usa exit() invece di return() per terminare una funzione non è per nulla uguale... :)

mjordan
26-05-2005, 18:34
:rotfl:
d'accordo con le differenze tra exit() e return() ;) anche se con la "funzione main" le differenze non sono tangibili, se si usa exit() invece di return() per terminare una funzione non è per nulla uguale... :)

Come insegna la fisica, tangibile non e' sinonimo di reale. Che non siano tangibili, quindi, non implica che non siano reali. :D

sirus
26-05-2005, 18:57
Come insegna la fisica, tangibile non e' sinonimo di reale. Che non siano tangibili, quindi, non implica che non siano reali. :D
e come direbbe Manera...
E' GIUUUSTOOO :sofico:

mjordan
26-05-2005, 19:14
e come direbbe Manera...
E' GIUUUSTOOO :sofico:

:D