processus et mémoire

virtual memory

Lorsque l’on lance un programme, celui-ci est chargé en mémoire afin d’être exécuté. Pour cela, un processus est créé. Ce processus ne possède pas d’accès direct à la mémoire physique de la machine. Mais il va avoir accès à une mémoire virtuelle d’une taille de 4 Go (sur machine 32 bits). Le processus est encapsulé dans une sandbox (bac à sable) afin d’être séparé des autres. Lors de son exécution, le processus pense être seul à s’exécuter sur la machine (ce qui n’est pas le cas).

page tables

Les tables de pages sont utilisées pour créer une interface entre la mémoire virtuelle (point de vue du processus) et la mémoire physique (point de vue machine). Chaque processus est associé à une table de page. Cette table associe les espaces mémoires virtuelles utilisés par le processus, avec des espaces physiques de la mémoire. C’est le kernel qui réalise cette tâche.

Gestion de la mémoire

On peut voir sur ce schéma que le processus 1 a accès à une mémoire virtuelle. Derrière celle-ci se cache le système de table de page vue précédemment.

memory segmentation

Lorsqu’un programme est chargé en mémoire afin de créer un processus la mémoire est segmentée.

Gestion de la mémoire

Section Déscription Taille
.text instructions du prog., read-only, là que commence le programme fixe
.data var. globales et statiques initialisées fixe
.bss var. globales et statiques non initialisées fixe

$ size ./rop # taille des sections + ajouter code

Zone mémoire Déscription Adresses Taille
stack var. locales des fonctions, cadre de pile (stack frame) croissantes variable
heap pour allocation dynamique du programmeur (malloc, calloc) décroissantes variable

stack frame : zone mémoire dans la pile, avec toutes les informations nécessaires à l’appel de cette fonction + var. locales de la fonction.

Exemple

On peut utiliser la commande size afin de connaitre la taille des séctions.

// main.c
#include <stdio.h>

int main (int argc, char* argv[]) {

  return 0;
}
$ gcc main.c -o a.out
$ size a.out
text	   data	    bss	    dec	    hex	filename
1537	    544	      8	   2089	    829	a.out

On peut voire la taille des séctions de notre exécutable de départ.

// main.c
#include <stdio.h>

int var_global;

int main (int argc, char* argv[]) {
  // ou static int var_static;
  return 0;
}
$ gcc main.c -o a.out
$ size a.out
text	   data	    bss	    dec	    hex	filename
1537	    544	     12	   2089	    829	a.out

La variable var_global est non initialisée, elle se retrouve donc dans la section .bss. Même chose si nous avions définie une varaible static dans le main().

// main.c
#include <stdio.h>

int var_global_1 = 10;

int main (int argc, char* argv[]) {
  
  return 0;
}
$ gcc main.c -o a.out
$ size a.out
text	   data	    bss	    dec	    hex	filename
1537	    548	      8	   2089	    829	a.out

La variable var_global est maintenant initialisée, elle se retrouve donc dans la section .data.