1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82
|
Structure de la mémoire
Segment de code ( aussi appelé segment de texte) ==> Adresse basse (ex : 0x000000)
CONTIENT : le code en lecture seule.
Le processeur utilise le registre d'offset EIP (instruction pointer, cf. ASM).
1- Il lit la première instruction pointé par IP
2- Ajoute à IP la taille en octet de l'instruction (bah oui, on va pas lire 5000 la même).
3- Exécute l'instruction lue en (1)
4- Retourne a l'étape (1).
Évidemment ce segment est en lecture seul, on va pas modifier le programme pendant son execution quand meme oO (ps : meme si on essaye ca fait une méchante erreur). Grace a ca, on peut avoir plusieurs execution d'un meme programme [à méditer].
Segment de donnée
CONTIENT : Les variables statiques et globales INITIALISES.
Segment bss
CONTIENT : Les variables statiques et globales NON-INITIALISES.
EXPLICATION : Les trois segments précédents ont une taille fixe, c'est ce qui permet aux variables globales et statiques de persister. Pourquoi ? Car ca ne fonctionne comme la pile, cf. bloc d'activation. No stress, vous comprendrez plus tard.
Segment de tas Adresses basses, grandit vers les adresses hautes.
CONTIENT : Les mallocs(), càd les allocations dynamiques, en effet ce segment ci n'a pas une taille fixe, donc on alloue (malloc, calloc) et puis libère (free).
--
--
--
\/
/\
--
--
--
Segment de pile Adresses mémoires hautes (ex = 0xFFFFFF), augmente vers les adresses basses (bon ca va, on aura compris je crois)
CONTIENT : les variables locales, et le contexte des appels de fonctions.
Bon pour bien comprendre il faut expliquer ça,
...
[S'hydrate au pepsi]
...
Reprenons le code du site :
12345678910 |
int triple(int nombre){
int resultat = 0;
resultat = 3 * nombre;
return resultat;}
int main(int argc, char *argv[]){
printf("Le triple de 15 est %d\n", triple(15));
return 0;
} |
Contexte de fonction : Ici le contexte de main() c'est les 2 instructions (placée dans le segment de donnée)
Le contexte de triple() : c'est les variables locales a triple() (au CONTEXTE de triple()) (placée dans le segment de pile) et les instructions.
Bon maintenant suivons le programme, il fait triple(15), très bien il va faire appel a la fonction triple, il y va et fait les instructions qu'il y a a l'intérieure. Mais il s'est perdu... Comment peut-il savoir comment revenir dans le programme main() et continuer ??
C'est la qu'intervient l'astuce, une fois que main() fait appel à notre fonction triple(), on va sauvegarder le contexte de main() sur la pile.
Cette sauvegarde est appelé bloc d'activation.
Retenez bien qu'il faut toujours des adresses et donc un pointeur pour tout et n'importe quoi (les variables, instructions, etc).
Le bloc d'activation contient donc :
1- Un pointeur sur le bloc d'activation (son adresse) ; SFP (save frame pointer).
Il pointera donc sur l'adresse de main() quand il chez triple(). [Segment de pile]
2- Un pointeur sur l'instruction que le processeur doit executé après l'appel de la fonction : l'adresse de retour (ret ca vous dit rien en assembleur? :king:).ex = mov IP, ret [Segment de code]
3- Les variables locales, instructions...
1- et 2- sont appelés prologue de fonction
Evidemment vous devez remarqué quelque chose, un élément manquant, si SFP pointe sur main(), QUI montre au processeur le bloc d'activation dans lequel ils sont en train de travailler ? Et bien c'est EBP (base pointer) aussi appelé.... EFP (frame pointer). En fait SFP ne sert qu'a sauvergarder, une fois de retour dans notre fonction main() c'est EBP qui prendra le relais :
ex = mov ebp, SFP
La pile va donc contenir plein de blocs d'activation si on a plein de fonction :D
Remarque : Voila pourquoi une variable locale ne peut être accedée que dans son contexte, car imaginons notre fonction triple, il ne peut pas accéder au bloc d'activation de main(), car il est en dessous de lui et que BP pointe sur lui ( et que du coup si on dit a BP d'aller pointer sur main() ca n'aura plus rien avoir avec notre fonction triple()). Au contraire nos variables statics et globales stockées sur le segment de données ou bss sont bien accessibles a tous les blocs d'activations !
Ca parait compliqué mais ca ne l'est pas tant que ca. Et c'est fini Very Happy ! |
Partager