[C] fork() - étudier la mémoire

fork() - étudier la mémoire [C] - C - Programmation

Marsh Posté le 29-12-2004 à 10:02:36    

J'aimerais voir un peu comment se fait la gestion de la mémoire lors d'un fork(). On m'a dit qu'aprés le fork le processus fils partage les zones mémoires du père tant que l'un de deux ne la modifie pas. Dans le cas d'une modification le fils créérais une zone propre à lui-même. Pour cela j'ai écris un petit programme.  
 
Le problème est qu'il est bien trop rapide lorsque j'essaie de visualiser son exécution avec top (sous linux), est-ce qu'avec gdb j'arriverais à mieux voir le phénomène ?
 
Voici le prog:
 

Code :
  1. #include <stdlib.h>
  2. #include <stdio.h>
  3. #include <unistd.h>
  4. #define SIZE 1024*1024*10
  5. int
  6. main (void)
  7. {
  8.   char *ptr = NULL;
  9.   long long i;
  10.   long long j;
  11.   ptr = malloc (sizeof (char) * SIZE);
  12.   for (i = 0; i < SIZE; i++)
  13.     ptr[i] = 0;
  14.   if (!fork ())
  15.     {
  16.       i = 0;
  17.       for (i = 0; i < SIZE; i++)
  18. {
  19.   for (j = 0; j < 2000; j++)
  20.     {
  21.       j++;
  22.       j--;
  23.     }
  24.   ptr[i] = 1;
  25. }
  26.       printf ("done\n" );
  27.     }
  28.   while (1);
  29. }


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 29-12-2004 à 10:02:36   

Reply

Marsh Posté le 29-12-2004 à 11:13:01    

avec gdb tu va faire du pas a pas. Tu vas voir chaque passage dans tes boucles for.
 
j'ai pas verifie mais je ne sais pas comment il se comporte notre ami gdb dans un prg multi process.
C'est sur que tu peux regarder les valeurs des variables et avoir leur implantation memoire (virtuelle ?) mais au dela de ca.. auras tu le pid ?
 
j'ai toujours eu en tete qu'a l'exec du fork le processus fils a son propre espace memoire virtuelle qui est une copie du contexte du parent.
Il connait les descripteurs ke le parent connait, variables, etc.
Mais les deux espaces memoire virtuelle des processus sont distincts physiquement.
 
Ca me parait d'ailleurs logique puisque suivant que l'ordonnanceur donne la main au fils ou au pere le retour du fork est different.
 
Enfin, la fonction fork est kan meme sencee dupliquer le process. C d'ailleurs explicite par le cas d'erreur possible en sortie :
 

Code :
  1. #include <errno.h>
  2. ...
  3. pid_t result;
  4. result = fork();
  5. if(errno==EAGAIN) printf("Memoire insuff pour dupliquer le processus\n" );
  6. /* Mais la memoire peut se liberer, il faut donc recommencer le fork... utiliser un do/while par exemple */


 
j'espere avoir repondu a ta question.. meme si ce n'est pas sur cette voie que tu cherchais.


Message édité par Truk le 29-12-2004 à 11:14:13
Reply

Marsh Posté le 29-12-2004 à 12:51:22    

Au moment où le fork est fait, seule une petite partie de la mémoire est dupliquée. En fait la gestion de la mémoire fonctionne par bloc et dés qu'un bloc est différent pour les deux processus il est dupliqué mais tant que le bloc est identique pour les deux processus il ne sera pas dupliqué.
 
Donc tant qu'il n'est pas nécessaire de dupliquer une zone mémoire elle ne le sera pas.
 
J'espère ne pas avoir dit trop de conneries.
 


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
Reply

Marsh Posté le 29-12-2004 à 12:53:08    

Sinon "man fork" explique bien ce qu'il se passe :

FORK(2)                                            Manuel du programmeur Linux                                           FORK(2)
 
NOM
       fork - Créer un processus fils.
 
SYNOPSIS
       #include <sys/types.h>
       #include <unistd.h>
 
       pid_t fork(void);
 
DESCRIPTION
       fork  crée  un  processus  fils qui diffère du processus parent uniquement par ses valeurs PID et PPID et par le fait que
       toutes les statistiques d’utilisation des ressources sont remises à zéro. Les verrouillages de fichiers, et  les  signaux
       en attente ne sont pas hérités.
 
       Sous  Linux,  fork  est implementé en utilisant une méthode de copie à l’écriture.  Ceci consiste à ne faire la véritable
       duplication d’une page mémoire que lorsqu’un processus en modifie une instance. Tant qu’aucun des deux processus  n’écrit
       dans  une  page  donnée, celle‐ci n’est pas vraiment dupliquée.  Ainsi les seules pénalisations induites par fork sont le
       temps et la mémoire nécessaires à la copie de la table des pages du parent ainsi que la création d’une structure de tâche
       pour le fils.
 
VALEUR RENVOYÉE
       En  cas  de succès, le PID du fils est renvoyé au processus parent, et 0 est renvoyé au processus fils. En cas d’échec -1
       est renvoyé dans le contexte du parent, aucun processus fils n’est créé, et errno contient le code d’erreur.
 
ERREURS
       EAGAIN fork ne peut pas allouer assez de mémoire pour copier la table des pages du père et une structure de tâche pour le
              fils.
 
       ENOMEM fork a échoué car le noyau n’a plus assez de mémoire.
EXEMPLE
           pid_t   pid;
 
           pid = fork ();
 
           if (pid > 0) {
                /* Processus père      */
           } else if (pid == 0) {
                /* Processus fils      */
           } else {
                /* Traitement d’erreur */
           }
 
CONFORMITÉ
       L’appel‐système fork est conforme à SVr4, SVID, POSIX, X/OPEN, BSD 4.3.
 
VOIR AUSSI
       clone(2), execve(2), vfork(2), wait(2).
 
TRADUCTION
       Christophe Blaess, 1996‐2003.
 
LDP                                                      18 juillet 2003                                                 FORK(2)


---------------
Le site de l'année :D (XHTML 1.0 strict) : http://darkoli.free.fr/index.html
Reply

Marsh Posté le 29-12-2004 à 14:06:04    

Mais comment pouvoir observer visuellement ce phénomène ?


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 29-12-2004 à 21:29:25    

Personne n'a d'idée  :??:


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 29-12-2004 à 21:33:41    

averc un sleep.  
 
Le processus père crée un grand tableau (plusieurs pagesà qu'il rempli. On va voir sa mémoire monter.
 
Puis il forke, le fils va faire des modifications au fur et à mesure dans le grand tableau au hasard (avec un sleep entre chaque écriture pour laisser le temps de voir), et chaques fois qu'il écrit dans une page ou il n'a pas encore écrit, sa mémoire prise augmente.
 
Par contre, je me souviens plus de la taille d'une page mémoire sous linux. Il me semble qu'elle fait 4ko ou 4Mo, suivant le système d'addressage utilisé.  
 


---------------
brisez les rêves des gens, il en restera toujours quelque chose...  -- laissez moi troller sur discu !
Reply

Marsh Posté le 30-12-2004 à 00:08:39    

Elle est de 4ko sur processeur 32bit et 8ko sur 64bit :jap:


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 30-12-2004 à 03:59:35    

4Ko en standard, 4Mo en mode PAE pour les x86, mais à vérifier si en PAE on le choix ou pas.
 
je crois qu'en x86-64 les pages font 4Mo.

Reply

Marsh Posté le 30-12-2004 à 04:00:22    

nodus >> tu parles du x68-64, ou des PowerPC par exemple ?
 
(je peux être complètement à coté de la plaque)

Reply

Marsh Posté le 30-12-2004 à 04:00:22   

Reply

Marsh Posté le 30-12-2004 à 14:37:22    

Pour le 8ko c'est sur des sparcs, j'imagine que ça doit être pareil sur les autres architectures 64bit.


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 30-12-2004 à 15:11:56    

H.S.
Sous HP-UX, chaque programme peut avoir une valeur différente, entre 4K et 256M (ça c'est de la page!) avec ça:
http://docs.hp.com/en/B2355-90689/chatr.1.html

Reply

Marsh Posté le 30-12-2004 à 15:49:39    

Lam's a écrit :

H.S.
Sous HP-UX, chaque programme peut avoir une valeur différente, entre 4K et 256M (ça c'est de la page!) avec ça:
http://docs.hp.com/en/B2355-90689/chatr.1.html


 
oui & non, c'est sur une machine en PA-RISC, ou un Itanium (qui est je crois compatible naturellement avec les binaires PA-RISC).
 
la taille des pages est complétement dépendantes des capacitées du MMU du CPU. ie ce n'est pas une caractéristique propre à un OS.


Message édité par bjone le 30-12-2004 à 15:50:12
Reply

Marsh Posté le 30-12-2004 à 15:54:22    

autant pour moi, en x86-64 même en long mode, les pages restent à 4Ko:
http://lwn.net/2001/features/OLS/pdf/pdf/x86-64.pdf

Reply

Marsh Posté le 30-12-2004 à 15:59:33    

bjone a écrit :

oui & non, c'est sur une machine en PA-RISC, ou un Itanium (qui est je crois compatible naturellement avec les binaires PA-RISC).
 
la taille des pages est complétement dépendantes des capacitées du MMU du CPU. ie ce n'est pas une caractéristique propre à un OS.


D'une part, c'est effectivement sur PA-RIC.
 
D'autre part, effectivement, sur x86 c'est soit 4K soit 4M. L'itanium tourne à 16K, mais il faudrait que je vérifie (j'ai tendance à me mélanger les pinceaux entre la segmentation et le paging).
 
 

Reply

Marsh Posté le 30-12-2004 à 18:43:51    

Et sur architecture Power, les pages font 4kB ou 16MB, au choix. Et bientot il y aura des pages de 16kB et 16GB.

Reply

Marsh Posté le 30-12-2004 à 19:18:13    

16Go de page ? mmm.... c'est voir à très long terme :D (ou alors pour du mapping d'espace de périph)

Reply

Marsh Posté le 30-12-2004 à 19:50:05    

Par contre en ayant exécuté mon programme j'ai remarqué que le printf n'était pas exécuté, c'est normal selon vous :??:


---------------
Et toi, tu crois que les ours polaires vont chier dans les bois ?
Reply

Marsh Posté le 12-02-2005 à 19:38:10    

darkoli a écrit :

Au moment où le fork est fait, seule une petite partie de la mémoire est dupliquée. En fait la gestion de la mémoire fonctionne par bloc et dés qu'un bloc est différent pour les deux processus il est dupliqué mais tant que le bloc est identique pour les deux processus il ne sera pas dupliqué.
 
Donc tant qu'il n'est pas nécessaire de dupliquer une zone mémoire elle ne le sera pas.
 
J'espère ne pas avoir dit trop de conneries.


 
Donc si je comprends bien :
 
Mettons que j'initialise un grand tableau de milliers d'entrées.
Je fais un fork. Le processus fils ne fera que consulter ce tableau, jamais il n'écrira dedans. Conclusion : le tableau ne sera pas dupliqué et sera simplement partagé entre les 2 processus ce qui évite un grand gaspillage de mémoire?
 
Si oui ç'est très intéressant, car je comptais utiliser une shared memory pour éviter la duplication du tableau. Mais comme il n'est pas dupliqué, pas besoin d'avoir recours à ce genre d'astuce.
 
J'ai bien tout compris? :)


Message édité par djmalo le 12-02-2005 à 19:38:47
Reply

Marsh Posté le 12-02-2005 à 20:17:53    

tout à fait mon colonel...
 
sauf que si le processus père écrit dans le tableau (dumoins l'espace alloué), le fils verra toujours les anciennes données.
 
donc si tu veux que le père produise, et le fils consomme, ça marchera pas.

Reply

Marsh Posté le 15-02-2005 à 21:38:46    

Il me semble que des erreurs on été dite...
 
D'après ce que je sais et ce que j'ai expérimenté sur cette fonction
 
fork() creer donc un processus identique à celui du pere...
Le fils hérite des fichiers ouverts, mais n'a pas la même zone mémoire alouée, ce qu'il fait qu'il n'ont aucune zone mémoire commune...
Pour faire communiqué deux processus ayant un lien de parenté, il existe les ipc
 
=> les pipes
=> les files de messages
=> les sémaphores (pour la synchronisation)
=> les mémoires partagées (shared memory)
=> connexion par réseau  
 
chaque processus à donc son propre SEGMENT de DONNEE, SEGMENT de PILE et SEGMENT de CODE (DS, SS et CS)...
 
donc je suis kazi certain à 99% qu'au départ, le fils n'aura pas accès aux zone mémoire du père puisque ce n'est pas la même zone mémoire qui lui ait allouée...  
j'ai eu l'occasion de testé cela il y a deux mois de ça sur une grosse application UNIX que j'ai du faire...
Donc si tu veux vraiment faire communiqué les deux processus, je te conseille la mémoire partagée, voir les files de messages...


Message édité par moi23372 le 15-02-2005 à 21:41:14
Reply

Marsh Posté le 15-02-2005 à 21:45:20    

Merci pour ces lumières mais en fait pour des questions d'optimisation, la plupart des implémentations ne procédent pas à une copie compléte des données/pile/tas du père sachant qu'un fork est souvant suivi par un exec. Elles utilisent à la place une technique appelée copy-on-write (COW) : elles sont partagées par le père et le fils et ce n'est que quand l'un des 2 tente de modifier l'une de ces régions que le noyau fait une copie de cette région seulement.
Et si tu fais un vfork là le fils fonctionne dans l'espace d'adressage du père, c'est super rigolo.


Message édité par manatane le 15-02-2005 à 21:46:44
Reply

Marsh Posté le 16-02-2005 à 20:41:57    

ah tiens si j'avais su ça avant je crois que ça m'aurais facilité pas mal de truc pour l'examen...

Reply

Marsh Posté le    

Reply

Sujets relatifs:

Leave a Replay

Make sure you enter the(*)required information where indicate.HTML code is not allowed