Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Étrange comportement des processus...

11 réponses
Avatar
booli_101
Lorsque l'on crer un processus à l'intérieur d'un programme grâce à la
fonction système 'fork', le processus fils partage maintenant les
fichiers ouverts part le processus parent.

Jusqu'à maintenant, pas de problème.

Si dans le processus parent, on le force à attendre la fin du
processus fils (wait()) avant d'écrire dans le fichier et qu'un
fclose(fichier) à été fait dans le fils, le parent à toujours la
possibilité d'écrire dans le fichier et les deux lignes sont présentes
lorsque l'on édite le fichier.

Hypothèse : le fichier prends du temps à ce fermer et le parent à le
temps de se glisser pour écrire mais la fonction sleep(10) vient
refuter cette hypothèse.

Hypothèse : le fclose n'a pas vraiment fonctionné... mais l'affichage
du retour de fonction vient refuter cette deuxième hypothèse.

Hypothèse : c'est l'algo d'ordenancement de l'OS et/ou la version du
compilateur qui est bogué. J'ai tenté l'exécution de ce programme sous
RedHat, gcc 3.2.2 20030222 (RedHat Linux 3.3.3-5) et le même phénomène
se produit.

Voici un exemple de ce que j'avance (essayez-le et envoyez-moi vos
commentaires et/ou explications) :
/*Système d'exploitation : SunOS 5.8*/
/*Compilateur : gcc version 2.95 19990728(release)*/
#include <stdio.h>
#include <unistd.h>

main() {
int pid;
int retourExit;
FILE *fd;
if ((fd = fopen("test","w")) != NULL) {
pid = fork();
}
else {
printf("Erreur d'ecriture");
exit(-1);
}

if (pid == 0) {/*On est dans le fils*/
fprintf ( fd, " Je suis le fils \n");
printf("Retour du fclose : %d", fclose(fd));
exit(0);
}
else {
wait(&retourExit); /*On attends l'exécution du fils avant de
poursuivre*/
sleep(10);
fprintf ( fd, " Je suis le père \n");
}
}

J'espère que quelqu'un pourra trouver une explication, pour ma part,
j'ai épuisé les petites connaissances que j'ai dans le domaine.

-Merci

10 réponses

1 2
Avatar
Bertrand Mollinier Toublet
Booli wrote:

Lorsque l'on crer un processus à l'intérieur d'un programme grâce à la
fonction système 'fork', le processus fils partage maintenant les
fichiers ouverts part le processus parent.

Jusqu'à maintenant, pas de problème.

Si, un, quand meme. Tu n'as pas poste dans le groupe adequat pour

repondre a ta question. Afin de maximiser tes chances d'obtenir des
reponses satisfaisantes, je te conseille de reposter sur un groupe
consacre aux unixoides. fr.comp.lang.c se contente de discuter du
langage C tel que defini par la norme ISO, et non pas des nombreuses API
systeme et autres ecrites dans ce langage.
--
Bertrand Mollinier Toublet
OO Check out Nerd Boy
.||. by Joaquim Gandara
bb at http://www.Nerb-Boy.net

Avatar
Anthony FLEURY
Booli wrote:

Lorsque l'on crer un processus à l'intérieur d'un programme grâce à la
fonction système 'fork', le processus fils partage maintenant les
fichiers ouverts part le processus parent.

Jusqu'à maintenant, pas de problème.



Cette question aurait plutot sa place sur un groupe unix. Tu vas te faire
taper sur les doigts à parler d'unix ici, car toute discussion doit se
faire sur des choses indépendantes de la plateforme.

<HS Total>
Petite indication cependant, le comportement est tout à fait normal. En
fait, le père et le fils partagent la même table de descripteurs de
fichier, ok. À sa création, le fils possède une table des descripteurs de
fichier qui pointe sur la même table que son père. Tout ce qui est offset
dans le fichier est dans cette table, donc si tu avances avec l'un des
deux, tu avances avec l'autre. Cependant, si tu tentes de fermer un
fichier, il ne le sera réelement que lorsque tous les processus l'utilisant
l'auront fermés. Pour l'analogie, c'est un peu comme unlink pour les
fichiers. Tant que tu as toujours au moins un lien sur le fichier dans ta
table des descripteurs, il ne sera pas effectivement fermé.
<HS>

--
Anthony FLEURY
Testing can show the presense of bugs, but not their absence.
-- Edsger W. Dijkstra

Avatar
Willy
<HS Total>
fichier, ok. À sa création, le fils possède une table des
descripteurs de fichier qui pointe sur la même table que son père.


donc faire un fflush () ?

bon, celà dit, si je me souviens bien, qd on fait un fopen, ça peut poser
des problèmes avec fork.
Il faut redescendre plus bas pour partager correctement les descripteurs de
fichier, voir la commande "open".

Mais je n'en suis pas du tout sûr ;-)

bon courage
@+
Willy

Avatar
espie
In article <c0eauu$45s$,
Willy wrote:
<HS Total>
fichier, ok. À sa création, le fils possède une table des
descripteurs de fichier qui pointe sur la même table que son père.


donc faire un fflush () ?

bon, celà dit, si je me souviens bien, qd on fait un fopen, ça peut poser
des problèmes avec fork.
Il faut redescendre plus bas pour partager correctement les descripteurs de
fichier, voir la commande "open".

Mais je n'en suis pas du tout sûr ;-)


Moi j'en suis nettement plus sur, et on redeviendrait presque in-charte.

Les fonctions de la bibliotheque standard C, comme fopen(3) et consorts,
en bonnes fonctions de bibliotheques, sont entierement implementees (en
general) en C relativement independant du systeme. Sur un systeme Unix,
elles ne vont guere `que' traduire des arguments, ET rajouter une gestion
de tampon, donc allocation memoire, et quelques variables correspondantes:
un FILE *, c'est un vrai fichier ouvert (file descriptor sous Unix), plus
quelques drapeaux, deux/trois pointeurs, et un ou deux tampons (et
eventuellement des indications de synchronisations avec d'autres flux, en
particulier si la bibliotheque `connait' C++, mais je m'egare).

Arrive fork(2), qui est un gentil appel systeme, qui ignore totalement
l'existence de la bibliotheque standard C, qui le lui rend bien,
d'ailleurs.

Consequence: toute cette savante gestion de tampons, ou on depose d'abord
ce qu'on doit ecrire en memoire jusqu'a en avoir assez, est totalement
ignoree par le fork(2).

Application:
int main()
{
printf("toto");
fork();
printf("n");
exit(0);
}

va assez souvent afficher deux fois toto sur un systeme Unix.

Parfaitement normal et rigoureux cote norme, vu que fork() est inconnu
de la norme C, et a donc le droit de generer du comportement non-standard.

Le fflush(stdout) ou fflush(NULL) permet de retomber sur nos pattes pour
les flux en sortie. C'est plus complique pour les flux en entree, pour
lesquels fflush() ne fonctionne pas (garantie), et ou on ne peut pas
faire grand chose cote norme C (il y a une fonction similaire POSIX qui
sait plus ou moins faire).

La solution souvent utilisee consiste a gerer les flush au niveau
file-descriptor avant le fork(), quitte a faire un fdopen() apres pour
pouvoir beneficier des joies de la bibliotheque standard.


Avatar
Antoine Leca
En c0ue45$1tuh$, Marc Espie va escriure:
int main() {
printf("toto");
fork();
printf("n");
exit(0); }

va assez souvent afficher deux fois toto sur un systeme Unix.

Parfaitement normal et rigoureux cote norme, vu que fork() est inconnu
de la norme C, et a donc le droit de generer du comportement
non-standard.


Qui plus est, ce code est parfaitement standard, à partir du moment où on
dit que fork() « clone » le processus, donc nous avons en fait ici deux
programmes (identiques) au sens de la norme C. Les deux affichent, cela me
paraît tout-à-fait conforme.

Non ?


Antoine

Avatar
Guillaume
Qui plus est, ce code est parfaitement standard, à partir du moment où on
dit que fork() « clone » le processus, donc nous avons en fait ici deux
programmes (identiques) au sens de la norme C. Les deux affichent, cela me
paraît tout-à-fait conforme.


Bien sûr, mais il ne me semble pas que la norme parle du concept de
"processus". Donc, le comportement de ce code est tout à fait
prédictible, mais ce n'est pas la norme seule qui peut le prédire.

Au fait, moi j'aurais plutôt utilisé la valeur de retour de fork().
Cet exemple donne de très mauvaises habitudes (ne pas tester les
valeurs de retour). La tester aurait permis:

- de savoir si la création du processus enfant a réussi ou non;
- de savoir si la suite du code s'éxecute dans le processus enfant
ou dans le processus parent.

Avatar
espie
In article <40338c79$0$5909$,
Guillaume wrote:
Au fait, moi j'aurais plutôt utilisé la valeur de retour de fork().
Cet exemple donne de très mauvaises habitudes (ne pas tester les
valeurs de retour). La tester aurait permis:

- de savoir si la création du processus enfant a réussi ou non;
- de savoir si la suite du code s'éxecute dans le processus enfant
ou dans le processus parent.


Ben voyons. De toutes facons en l'occurrence, le but du jeu etait de
montrer un comportement `aberrant'. On voit mal en quoi afficher
deux fois toto peut etre utile ici.

Avatar
Antoine Leca
En 40338c79$0$5909$, Guillaume va escriure:
Qui plus est, ce code est parfaitement standard, à partir du moment
où on dit que fork() « clone » le processus, donc nous avons en fait
ici deux programmes (identiques) au sens de la norme C. Les deux
affichent, cela me paraît tout-à-fait conforme.


Bien sûr, mais il ne me semble pas que la norme parle du concept de
"processus".


Bon, j'ai pas été assez clair, je vais donc développer.

fork() est défini par une norme, en l'occurence POSIX. Non ? Si. Son effet,
dans cette norme, est de « cloner » les processus. Non ? Si. Et un
processus, dans POSIX, c'est une notion bien définie. Sa représentation en
C, cela s'appelle un programme (c'est même l'objet de la norme C que de
définir ce que c'est qu'un programme). Donc où est le problème ?


Donc, le comportement de ce code est tout à fait prédictible,


Tu parles de l'original (celui de 2Booli"), ou de l'exemple de Marc Espie ?
Parce que en ce qui concerne le second, c'est ton avis et tu le partages...
(Même Marc a pris de grosses précautions, « assez souvent »)

mais ce n'est pas la norme seule qui peut le prédire.


Allez, c'est reparti. On va encore avoir droit à « ceci n'est pas dans
l'objet de la norme C, donc 'raus »: Il m'avait semblé avoir vu la balise
[HS], pourtant...


Au fait, moi j'aurais plutôt utilisé la valeur de retour de fork().


Pourquoi faire ? pour la multiplier avec celle de printf ? ou avec celle de
fflush ? ;-)


Antoine


Avatar
Guillaume
Au fait, moi j'aurais plutôt utilisé la valeur de retour de fork().



Pourquoi faire ? pour la multiplier avec celle de printf ? ou avec celle de
fflush ? ;-)


Tout simplement parce que c'est avec la valeur de retour seulement que
l'on va rendre l'exécution de la suite du code parfaitement prédictible.
C'était juste une parenthèse, mais qui méritait d'être soulevée.

Rappelons juste que fork() renvoie le PID du processus enfant au
processus parent, 0 au processus enfant et -1 si la création du
processus enfant a échoué. Dans l'exemple donné (en supposant que
fork() réussisse), l'appel à printf() sera exécuté à la fois dans
le contexte du processus parent et dans le contexte du processus
enfant. Il y aura donc bien deux appels à printf().

Ce n'est donc qu'avec un test rigoureux de la valeur de retour de fork()
que l'on peut savoir exactement ce qui va se passer dans tout le code
qui s'exécutera après son appel.

En ce qui concerne le comportement spécifique des flots d'E/S partagés
par plusieurs processus (dans le sujet initial), il est très largement
dépendant du système d'exploitation sous-jacent. Il n'y a donc pas
de réponse univoque et d'ailleurs elle ne dépend pas du langage (ici C)
de programmation.


Avatar
espie
In article <4033bb01$0$5909$,
Guillaume wrote:
Au fait, moi j'aurais plutôt utilisé la valeur de retour de fork().



Pourquoi faire ? pour la multiplier avec celle de printf ? ou avec celle de
fflush ? ;-)


Tout simplement parce que c'est avec la valeur de retour seulement que
l'on va rendre l'exécution de la suite du code parfaitement prédictible.
C'était juste une parenthèse, mais qui méritait d'être soulevée.


Il y a une erreur de logique dans ton raisonnement.
Tu ne peux pas savoir a l'avance si ton fork() va renvoyer -1 ou reussir.

Tu m'expliqueras dans ces conditions comme le fait de sauver cette valuer
de retour va rendre l'execution de la suite du code `parfaitement
predictible'.

De toutes facons, on ne sait deja pas avec certitude au moment de
printf("toto'); ce que fait l'implementation de la bibliotheque C.
(on peut savoir ce que fait une implementation donnee, bien sur).

Partant de la, le comportement de la suite aura bien du mal a etre
predictible...



1 2