Partage
  • Partager sur Facebook
  • Partager sur Twitter

Fork(), gestion de la mémoire étrange

Adresse stack identique adresse heap différente

Sujet résolu
1 février 2023 à 9:03:49


#include <iostream>
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>

using std::cout; using std::endl;

int main() {
    pid_t c_pid = fork();
	char *ptr = new char(2);
	char stack = 'x';
	ptr[0] = 'a';

    if (c_pid == 0) 
	{
		std::cout <<  &ptr << std::endl;
    cout << "printed from parent process " << getpid() << endl;
		stack = 'y';
		ptr[0] = 'b';
	}
	else {
		waitpid(-1, NULL, 0);
	}	
	if (c_pid == 0)
	{
		printf("enfant adresse pointe par le pointeur: %p\n",ptr);
		printf("enfant adresse pointer: %p\n",&ptr);
		std::cout << "enfant heap: " << ptr << std::endl;
		std::cout << "enfant stack: " << stack << std::endl;
	}
	if (c_pid != 0)
	{
		printf("parent adresse pointe par le pointeur: %p\n",ptr);
		printf("parent adresse pointeur: %p\n",&ptr);
		std::cout << "parent heap: " <<  ptr << std::endl;
		std::cout << "parent stack: " << stack << std::endl;
	}
	delete ptr;
   }

 Bonjour, j'ai ce programme qui affiche l'adresse de mes variables que l'on soit dans le fork ou dans le parent.

J'ai crée ce programme car ayant besoin de faire un fork dans un autre programme je devait savoir si je devais delete ma mémoire dynamique dans chaque processus ou seulement une fois afin d’éviter les leaks ou de ne pas faire de double delete sur une même adresse de la heap, voici mon output :

ydumaine@k1r2p5 test % ./a.out      
0x7ffee870f850
printed from parent process 59084
enfant adresse pointe par le pointeur: 0x7fad33d04220
enfant adresse pointer: 0x7ffee870f850
enfant heap: b
enfant stack: y
parent adresse pointe par le pointeur: 0x7fad33c05890
parent adresse pointeur: 0x7ffee870f850
parent heap: a
parent stack: x



 J'ai entendu parler du copie-on-write qui conserve la même adresse mémoire pour les deux processus jusqu’à ce que le fork écrive dans cette adresse.

Ma question : les variables de la Heap sont bien différentes, donc la heap a été copié et la valeur de ptr changé. Mais la valeur de l'adresse de la variable stack est identique dans le fork et le parent, même lorsque je modifie leur contenue. J'ai entendue parler des adresse virtuels mais je pense que je ne suis pas concerné puisque quand je lance deux fois d'affiler le même programme j’obtiens des adresses différentes a chaque fois. Pourquoi les adresse de la ma variable stack restent identique ?

J'ai utilisé printf car je pensais que c’était std::cout qui déconnait mais cela ne change rien, je suis sur macOS catalina

-
Edité par YannUFLL 1 février 2023 à 9:08:15

  • Partager sur Facebook
  • Partager sur Twitter
1 février 2023 à 16:51:56

Je suppose que tu sais qu'aucun dev C++ n'ecrirait ce genre de code. C"est plus du C que du C++. 

Et sinon, a priori, si, c'est des adresses virtuelles. Chaque process a sa propre table d'adressage, je pense (sans avoir étudiée en détail ce genre de problématique).

  • Partager sur Facebook
  • Partager sur Twitter
1 février 2023 à 18:19:19

Bien entendue ce programme est dans la forme plutôt du C. OK donc si les adresses sont différentes sur la heap c'est parce que il n'y a pas de table d'adressage virtuel de ce coté là je suppose.
  • Partager sur Facebook
  • Partager sur Twitter
1 février 2023 à 23:17:58

Bonjour,

Ce que fait le fork():
- avant le fork(), il n'y a qu'un seul process.
- au moment du fork(), tout est copié pour obtenir 2 process dont le contenu est totalement identique. En particulier chaque variable a le même contenu, mais aussi la même adresse. Par exemple si on a pré-initialisé un pointeur son clone aura la même valeur.
Mais il s'agit bien de 2 process différents, donc forcément ces adresses identiques ne désignent pas la même mémoire, les adresses sont virtuelles. La heap aussi a été dupliquée.
- A partir de cet instant, chaque process va vivre sa vie indépendamment de l'autre.
Ainsi par exemple, il y avait plusieurs threads, eux aussi ont été dupliqués et continuent eux aussi leur déroulement dans chaque process.

Il y a quand même de toutes petites différences entre les 2 process:
- le fils sort du fork() avec la valeur 0, le père sort avec le pid du fils.
- chacun a sont propre pid.
- s'il y avait des handle ouverts comme par exemple une ouverture fichier, chaque process a accès a son handle, mais le driver correspondant a dû dupliquer donc chaque process est un client distinct du driver (si le driver ne supporte pas ça, le fork() échoue!)

  • Partager sur Facebook
  • Partager sur Twitter

En recherche d'emploi.

8 février 2023 à 10:46:09


Merci beaucoup pour ta réponse détaillée sur la duplication de variable en cas de fork. C'est très clair et informatif. Merci encore!

  • Partager sur Facebook
  • Partager sur Twitter
8 février 2023 à 11:51:09

Chaque processus (lourd) possède son propre espace mémoire virtuel, avec des adresses virtuelles qui peuvent aller théoriquement et en gros de 0 à 2^64 - 1 (sur architecture 64).

Ces adresses sont traduites, à l'exécution, en adresses réelles par un circuit  "memory management unit" qui utilise une table des pages (qui dit où se trouvent, en mémoire réelle, les pages de l'espace virtuel=/

Quand un processus est créé par fork, il possède une copie de l'espace mémoire virtuel de son père, avec les MEMES adresses virtuelles.  On peut le voir avec ça (C)

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

int main()
{
	int a = 33;
	if (fork() == 0 ) {
		printf("adresse pour le fils = %p\n", &a);
		return 0;
	}
	printf("adresse pour le père = %p\n", &a);
    return 0;
}

dont l'exécution affiche

$ ./adre 
adresse pour le père = 0x7fffa489363c
adresse pour le fils = 0x7fffa489363c




Mais les tables de pages sont différentes, donc une adresse virtuelle ne conduit pas  à la même adresse physique.

Plus de détails : enfin, pas forcément.  En fait le noyau du système emploie généralement une astuce qui consiste à NE PAS COPIER tout de suite le contenu de l'espace mémoire virtuel du père. A la place il construit une table des pages qui est une copie de celle du père - les pages sont donc partagées - mais en marquant les pages comme étant en lecture seule.

SI le père ou le fils tente de modifier une de ces pages, la MMU déclenchera une interruption etc, et le système modifiera la table des pages du coupable pour qu'elle désigne une copie de la page.   C'est le fameux "Copy On Write", parce que la copie est faite seulement au moment de la première tentative d'écriture. Tant qu'on ne fait que des lectures de la page d'origine, c'était pas la peine de dupliquer.

-
Edité par michelbillaud 8 février 2023 à 11:58:06

  • Partager sur Facebook
  • Partager sur Twitter