Differences

This shows you the differences between two versions of the page.

Link to this comparison view

sde:laboratoare:07_python [2020/04/01 13:44]
iuliana.marin created
— (current)
Line 1: Line 1:
-====== TP 07 - Gestion de la mémoire ====== 
-===== Gestion de la mémoire ===== 
- 
-Sle système de gestion de la mémoire d'un système d'​exploitation est utilisé par tous les autres sous-systèmes:​ planificateur,​ E / S, système de fichiers, gestion des processus, mise en réseau. La mémoire est une ressource importante, c'est pourquoi des algorithmes efficaces pour son utilisation et sa gestion sont nécessaires. 
- 
-Le rôle du sous-système de gestion de la mémoire est de: 
-  * garder une trace des zones de mémoire physique (occupée ou libre) 
-  * pour fournir aux processus ou autres sous-systèmes un accès à la mémoire 
-  * mapper les pages de mémoire virtuelle d'un processus (pages) sur les pages physiques (frames). 
- 
-Le noyau du système d'​exploitation propose un ensemble d'​interfaces (appels système) qui permettent l'​allocation / partage de mémoire, le mappage de régions de mémoire virtuelle sur des fichiers, le partage de zones de mémoire. 
- 
-Malheureusement,​ le niveau limité de compréhension de ces interfaces et des actions qui se produisent derrière elles conduit à un certain nombre de problèmes qui sont couramment rencontrés dans les applications logicielles:​ fuites de mémoire, accès invalides, écrasement,​ débordement de tampon, corruption de zone mémoire. 
- 
-Il est donc essentiel de connaître le contexte dans lequel le sous-système de gestion de la mémoire fonctionne et de comprendre l'​interface fournie au programmeur par le système d'​exploitation. 
- 
-===== L'​espace d'​adressage d'un processus ===== 
- 
-{{ so:​laboratoare-2013:​process_address_space.jpg|L'​espace d'​adressage d'un processus}} 
- 
-L'​espace d'​adressage d'un processus ou, mieux encore, l'​espace d'​adressage virtuel d'un processus est la zone de mémoire virtuelle qu'un processus peut utiliser. Chaque processus a son propre espace d'​adressage. Même dans les situations où deux processus partagent une zone mémoire, l'​espace virtuel est distinct, mais il est mappé sur la même zone mémoire physique. 
- 
-La figure suivante montre un espace d'​adressage typique pour un processus. Dans les systèmes d'​exploitation modernes, dans l'​espace virtuel de chaque processus, la mémoire du noyau est mappée, elle peut être mappée au début ou à la fin de l'​espace d'​adressage. Ensuite, nous ferons uniquement référence à l'​espace d'​adressage dans l'​espace utilisateur pour un processus. 
- 
-Les 4 zones importantes de l'​espace d'​adressage du processus sont la zone de données, la zone de code, la pile et le tas. Comme le montre la figure, la pile et le tas sont les zones qui peuvent croître. En fait, ces deux domaines sont dynamiques et n'ont de sens que dans le contexte d'un processus. De l'​autre côté, les données dans la zone de données et la zone de code sont décrites dans l'​exécutable. 
- 
-<note info> 
-Parce que Python est un langage interprété,​ toutes les variables sont affectées sur le tas. Pour pouvoir analyser la mémoire allouée, nous devons utiliser un langage compilé. Par conséquent,​ dans ce laboratoire,​ nous utiliserons des exemples en langage C. 
-</​note>​ 
- 
- 
-==== Zone de code ==== 
- 
-Le segment de code (également appelé ''​segment de texte''​) représente les instructions en langage machine du programme. Le registre du pointeur d'​instruction (IP) fera référence aux adresses dans la zone de code. Lisez l'​instruction indiquée par l'IP, décodez-la et interprétez-la,​ puis incrémentez le compteur de programme et passez à l'​instruction suivante. 
-La zone de code est généralement une zone en lecture seule afin que le processus ne puisse pas modifier ses instructions en utilisant le mauvais pointeur. La zone de code est partagée entre tous les processus exécutant le même programme. Ainsi, une seule copie du code est mappée à l'​espace d'​adressage virtuel de tous les processus. 
- 
-==== Zones de données ==== 
- 
-Les zones de données contiennent les variables globales définies dans un programme et les variables en lecture seule. Il existe plusieurs sous-types de zones de données selon le type de données. 
- 
-=== .data === 
- 
-La zone ''​ .data ''​ contient les variables globales et les variables statiques __initialized__ aux valeurs nulles d'un programme. Par exemple: 
- 
-<code c> 
-static int a = 3; 
-char b = '​a';​ 
-</​code>​ 
- 
-=== .bss === 
- 
-La zone ''​ .bss ''​ contient les variables globales et statiques __initialized__ d'un programme. Avant l'​exécution du code, ce segment est initialisé avec 0. Par exemple: 
- 
-<code c> 
-static int a; 
-char b; 
-</​code>​ 
- 
-En général, ces variables ne seront pas préallouées dans l'​exécutable,​ mais lors de la création du processus. L'​allocation de la zone ''​ .bss ''​ se fait sur des pages mises à zéro. 
- 
-=== .rodata === 
- 
-La zone ''​ .rodata ''​ contient des informations qui ne peuvent être lues que et non modifiées. Voici les __literals__ stockés: 
-<code c> 
-"​Hello,​ World!"​ 
-"En Taro Adun!" 
-</​code>​ 
- 
-et __contenu__. Toutes les variables globales déclarées avec le mot-clé ''​ const ''​ seront placées dans les ''​ données ''​. Les variables locales déclarées comme "​const"​ seront placées sur la pile, donc dans une zone mémoire non marquée comme // en lecture seule // et peuvent être modifiées par un pointeur sur elles. Un cas particulier est représenté par les variables locales constantes déclarées avec le mot-clé ''​ statique ''​ qui seront mises dans le ''​ .data '':​ 
-<code c> 
-const int a;           /* în .rodata */ 
-const char ptr[]; ​     /* în .rodata */ 
- 
-void myfunc(void) 
-{ 
-   int x;              /* pe stivă */ 
-   const int y;        /* pe stivă */ 
-    
-   ​static const int z; /* în .rodata */ 
-    
-   ​static int p = 8;   /* în .data */ 
-   ​static int q;       /* în .bss */ 
-   ... 
-</​code>​ 
- 
-==== Pile ==== 
- 
-La pile est une région dynamique au sein d'un processus, gérée automatiquement par le compilateur. 
- 
-La pile est utilisée pour stocker des "​cadres de pile". Un nouveau "cadre de pile" sera créé pour chaque appel de fonction. 
- 
-Un "cadre de pile" contient: 
-  * variables locales 
-  * arguments de fonction 
-  * adresse de retour 
- 
-Sur la grande majorité des architectures modernes, la pile croît (des grandes adresses aux petites adresses) et le tas augmente. La pile augmente à chaque appel de fonction et diminue à chaque retour de la fonction. 
- 
-La figure ci-dessous montre une vue conceptuelle sur la pile lors de l'​appel d'une fonction. 
- 
-{{ so:​laboratoare-2013:​call_stack.png }} 
- 
-==== Tas (Heap) ==== 
- 
-Le tas est la zone de mémoire dédiée à l'​allocation dynamique de mémoire. Le segment de mémoire est utilisé pour allouer des régions de mémoire dont la taille est déterminée lors de l'​exécution. 
- 
-Comme la pile, le tas est une région dynamique qui change sa taille. Contrairement à la pile, cependant, le tas n'est pas géré par le compilateur. Il est du devoir du programmeur de savoir combien de mémoire allouer et de se rappeler combien allouer et quand allouer. Les problèmes courants dans la plupart des programmes sont la perte de références aux zones allouées (fuites de mémoire) ou la référence de zones non allouées ou insuffisamment allouées (accès invalides). 
- 
-Dans des langages comme Java, Lisp, etc. là où il n'y a pas de "​liberté de pointeur",​ l'​effacement de l'​espace alloué se fait automatiquement via un garbage collector. Sur ces systèmes, le problème de la perte de références est évité, mais le problème de référence de zones non allouées reste actif. 
-  
-  
- 
-===== Allocation / affectation de mémoire ===== 
- 
-** L'​allocation de mémoire ** est effectuée statiquement par le compilateur ou dynamiquement pendant l'​exécution. // L'​allocation statique // est effectuée dans des segments de données pour les variables globales ou pour les littéraux. 
- 
-Lors de l'​exécution,​ les variables sont allouées sur la pile ou dans le tas. L'​allocation de pile est automatiquement effectuée par le compilateur pour les variables locales d'une fonction (à l'​exception des variables locales préfixées par l'​identifiant ** statique **). 
- 
-// L'​allocation dynamique // se fait en tas. L'​allocation dynamique se produit quand on ne sait pas, au moment de la compilation,​ combien de mémoire sera nécessaire pour une variable, une structure, un vecteur. Si, au moment de la compilation,​ on sait combien d'​espace une variable occupera, son allocation statique est recommandée,​ afin d'​éviter les erreurs qui se produisent fréquemment dans le contexte de l'​allocation dynamique. 
- 
-Afin de fragmenter le moins possible l'​espace d'​adressage du processus, à la suite d'​allocations et de désallocations de zones de différentes tailles, l'​allocateur de mémoire organisera le segment de données alloué dynamiquement en // tas //, d'où le nom du segment. 
- 
-** Débordement de mémoire ** signifie libérer de la zone de mémoire précédemment allouée (elle est marquée comme libre). 
- 
-Si le verrouillage d'une zone mémoire est omis, il restera alloué pendant toute la durée d'​exécution du processus. Chaque fois qu'une zone mémoire n'est plus nécessaire,​ elle doit être compensée pour l'​efficacité d'​utilisation de l'​espace mémoire. 
- 
-Il n'est pas nécessaire de traiter différents domaines avant un appel [[http://​linux.die.net/​man/​3/​exit|exit]] ou avant la fin du programme car ils sont automatiquement libérés par le système d'​exploitation. 
- 
-<​note>​Dans la plupart des cas, des problèmes surviennent si vous essayez de colocaliser deux fois la même zone de mémoire. En effet, les données de gestion interne des zones allouées sont corrompues. Habituellement,​ le noyau signale le problème avec une interruption synchrone.</​note>​ 
- 
-==== Allocation de mémoire sous Linux ==== 
- 
-Sous Linux, l'​allocation de mémoire pour les processus utilisateur se fait via les fonctions de bibliothèque [[http://​linux.die.net/​man/​3/​malloc|malloc]],​ [[http://​linux.die.net/​man/​3/​calloc|calloc]] et [[http://​linux.die.net/​man/​3/​realloc|realloc]],​ et sa collocation à travers la fonction [[http://​linux.die.net/​man/​3/​free|free]]. Ces fonctions représentent les appels de bibliothèque et résolvent, dans la mesure du possible, l'​allocation de mémoire et les demandes d'​allocation dans l'​espace utilisateur. 
- 
-<spoiler Implémentation de la fonction malloc> 
-\\  
-Implémentation de la fonction [[http://​linux.die.net/​man/​3/​malloc|malloc]] dépend du système d'​exploitation. 
- 
-Il existe des implémentations qui conservent des tables qui spécifient les zones de mémoire allouées au tas. S'il y a des zones libres sur le tas, appelez [[http://​linux.die.net/​man/​3/​malloc|malloc]] cil nécessite une zone de mémoire qui peut être encadrée dans une zone libre du tas sera satisfaite immédiatement,​ marquant la zone dans la table comme étant allouée et renvoyant au programme appelant un pointeur vers celle-ci. 
- 
-Si, au lieu de cela, une zone est demandée qui ne rentre dans aucune zone sans segment de mémoire, [[http://​linux.die.net/​man/​3/​malloc|malloc]] va tenter d'​étendre le tas par appel système [[http://​linux.die.net/​man/​2/​brk|brk]] ou [[http://​linux.die.net/​man/​3/​mmap|mmap]]. 
- 
-{{so:​laboratoare-2013:​heap_bounds.gif|}} 
- 
-Il existe des implémentations qui, pour chaque zone de mémoire requise avec [[http://​linux.die.net/​man/​3/​malloc|malloc]] ajoute un en-tête qui contient des informations utiles - taille d'une zone, pointeur vers la zone suivante, que la zone ait été effacée ou non. 
- 
-{{so:​laboratoare-2013:​heap_chunks.gif|}} 
- 
-Plus de détails sur malloc et comment organiser le tas [[http://​academicearth.org/​lectures/​heap-management|ici]] et [[https://​docs.google.com/​viewer?​url=http://​wiki-prog.kh405.net/​images/​0/​04/​Malloc_tutorial.pdf|ici]]. 
-</​spoiler>​ 
- 
-\\  
-<code c> 
-void *malloc(size_t size); 
-void *calloc(size_t nmemb, size_t size); 
-void *realloc(void *ptr, size_t size); 
-void free(void *ptr); 
-</​code>​ 
- 
-Toujours relâcher ([[http://​linux.die.net/​man/​3/​free|free]]) mémoire allouée. La mémoire allouée au processus est automatiquement libérée à la fin du processus, mais, par exemple, dans le cas d'un processus serveur qui s'​exécute depuis longtemps et ne libère pas la mémoire allouée, il finira par occuper toute la mémoire disponible dans le système, ce qui aura des conséquences néfastes. 
- 
-** Avertissement! ** Ne libérez pas deux fois la même zone de mémoire, car cela entraînerait la corruption des tables [[http://​linux.die.net/​man/​3/​malloc|malloc]] qui aura à nouveau des conséquences néfastes. Depuis la fonction [[http://​linux.die.net/​man/​3/​free|free]] renvoie immédiatement s'il reçoit en paramètre un pointeur ''​ NULL '',​ il est recommandé qu'​après un appel [[http://​linux.die.net/​man/​3/​free|free]],​ le pointeur est réinitialisé sur ''​ NULL ''​. 
- 
-Voici quelques exemples d'​utilisation de l'​allocation de mémoire [[http://​linux.die.net/​man/​3/​malloc|malloc]]:​ 
- 
-<code c> 
-int n = atoi(argv[1]);​ 
-char *str; 
- 
-/* généralement malloc reçoit l'​argument taille comme: 
-   ​num_elements * size_of_element */ 
-str = malloc((n + 1) * sizeof(char));​ 
-if (NULL == str) { 
-perror("​malloc"​);​ 
-exit(EXIT_FAILURE);​ 
-} 
- 
-[...] 
- 
-free(str); 
-str = NULL; 
- 
-</​code>​ 
- 
-<code c> 
- 
-/* Création d'un tableau de références aux arguments reçus par un programme */ 
-char **argv_no_exec;​ 
- 
-/* allouer de l'​espace pour le tableau */ 
-argv_no_exec = malloc((argc - 1) * sizeof(char*));​ 
-if (NULL == argv_no_exec) { 
-perror("​malloc"​);​ 
-exit(EXIT_FAILURE);​ 
-} 
- 
-/* définir des références aux arguments du programme */ 
-for (i = 1; i < argc; i++) 
-argv_no_exec[i-1] = argv[i]; 
- 
-[...] 
- 
-free(argv_no_exec);​ 
-argv_no_exec = NULL; 
-</​code>​ 
- 
-L'​appel [[http://​linux.die.net/​man/​3/​realloc|realloc]] est utilisé pour changer l'​espace mémoire alloué par un appel [[http://​linux.die.net/​man/​3/​malloc|malloc]] ou [[http://​linux.die.net/​man/​3/​calloc|calloc]]:​ 
- 
-<code c> 
-int *p; 
- 
-p = malloc(n * sizeof(int));​ 
-if (NULL == p) { 
-perror("​malloc"​);​ 
-exit(EXIT_FAILURE);​ 
-} 
- 
-[...] 
- 
-p = realloc(p, (n + extra) * sizeof(int));​ 
- 
-[...] 
- 
-free(p); 
-p = NULL; 
-</​code>​ 
- 
-L'​appel [[http://​linux.die.net/​man/​3/​calloc|calloc]] est utilisé pour allouer des zones de mémoire dont le contenu est nul (plein de valeurs nulles). Contrairement à [[http://​linux.die.net/​man/​3/​malloc|malloc]],​ l'​appel recevra deux arguments: le nombre d'​éléments et la taille d'un élément. 
- 
-<code c> 
-list_t *list_v; /* list_t could be any C type ( except void ) */ 
- 
-list_v = calloc(n, sizeof(list_t));​ 
-if (NULL == list_v) { 
-perror("​calloc"​);​ 
-exit(EXIT_FAILURE);​ 
-} 
- 
-[...] 
- 
-free(list_v);​ 
-list_v = NULL; 
-</​code>​ 
- 
-** Attention ** Selon la norme C, il est redondant (et considéré comme une mauvaise pratique) de faire // cast // à la valeur retournée par [[http://​linux.die.net/​man/​3/​malloc|malloc]]. 
-<code C> 
-int *p = (int *)malloc(10 * sizeof(int));​ 
-</​code>​ 
-malloc renvoie // void * // qui en C est automatiquement converti en le type souhaité. De plus, si le cast est fait et que l'​en-tête ''​ stdlib.h ''​ requis pour la fonction malloc n'est pas inclus, aucune erreur ne sera générée! Sur certaines architectures,​ ce cas peut conduire à un comportement indéfini. Contrairement à C, en C ++, vous avez besoin d'un cast. La discussion est élaborée [[http://​www.cprogramming.com/​faq/​cgi-bin/​smartfaq.cgi?​answer=1047673478&​id=1043284351 | ici ]]. 
- 
-Vous trouverez plus d'​informations sur les fonctions d'​allocation dans [[http://​www.gnu.org/​software/​libc/​manual/​html_node/​Unconstrained-Allocation.html#​Unconstrained-Allocationl| le manuel de bibliothèque standard C]] et dans la page de manuel **man malloc**. 
- 
-===== Travailler avec la mémoire - Problèmes ===== 
- 
-Travailler avec le tas est l'une des principales causes de l'​émergence de problèmes de programmation. Travailler avec des pointeurs, la nécessité d'​utiliser les appels système / bibliothèque pour l'​allocation / concession, peut conduire à un certain nombre de problèmes qui affectent le fonctionnement du programme (souvent fatal). 
- 
-Les problèmes les plus fréquemment rencontrés lors de l'​utilisation de la mémoire sont les suivants: 
-  * accès mémoire invalide - qui implique l'​accès à des zones qui n'ont pas été allouées ou qui ont été libérées. 
-  * fuites de mémoire - situations où la référence à une zone précédemment allouée est perdue. Cette zone restera occupée jusqu'​à la fin du procès. 
- 
-Les problèmes et les utilitaires qui peuvent être utilisés pour les combattre seront présentés ci-dessous. 
- 
-==== Accès invalide ==== 
- 
-En règle générale, l'​accès à une zone de mémoire non valide entraîne une erreur de page et l'​achèvement du processus (sous Unix, cela signifie l'​envoi du signal SIGSEGV -> affichage du message ''​ Erreur de segmentation ''​). Toutefois, si l'​erreur apparaît à une adresse non valide, mais sur une page valide, le matériel et le système d'​exploitation ne pourront pas signaler l'​action comme non valide. En effet, l'​allocation de mémoire se fait au niveau de la page. Par exemple, il peut y avoir des situations où seule la moitié de la page est utilisée. Bien que l'​autre moitié contienne des adresses non valides, le système d'​exploitation ne pourra pas détecter un accès non valide à cette zone. 
- 
-Ces accès peuvent entraîner une corruption de tas et une perte de cohérence de la mémoire allouée. Comme nous le verrons ci-dessous, il existe des utilitaires qui aident à détecter ces situations. 
- 
-Un type spécial d'​accès non valide est [[http://​en.wikipedia.org/​wiki/​Buffer_overflow|buffer overflow]]. Ce type d'​attaque implique de référencer des régions valides dans l'​espace d'​adressage du processus au moyen d'une variable qui ne devrait pas pouvoir référencer ces adresses. En règle générale, une attaque par dépassement de tampon entraîne l'​exécution de code non sécurisé. La protection contre les attaques par débordement de tampon est obtenue en vérifiant les limites d'un tampon / vecteur à la compilation ou au moment de l'​exécution. 
- 
-==== Fuites de mémoire ==== 
- 
-Une [[http://​en.wikipedia.org/​wiki/​Memory_leak|fuite de mémoire]] apparaît dans deux situations: 
-  * un programme omet d'​effacer une zone mémoire 
-  * un programme perd la référence à une zone mémoire allouée et, par conséquent,​ ne peut pas la libérer 
- 
-Les fuites de mémoire ont pour effet de réduire la quantité de mémoire existante dans le système. On peut atteindre, dans des situations extrêmes, la consommation de toute la mémoire du système et l'​impossibilité de fonctionnement de ses différentes applications. 
- 
-Comme pour le problème d'​accès à la mémoire invalide, [[#​Valgrind|Utilitaire Valgrind]] il est très utile pour détecter les fuites de mémoire d'un programme. 
- 
-==== Accord double ==== 
- 
-Le nom "​accord double"​ offre une bonne intuition sur la cause: libérer deux fois le même espace mémoire. La double désallocation peut avoir des effets négatifs car elle affecte les structures internes utilisées pour gérer la mémoire occupée. 
- 
-Dans les dernières versions de la bibliothèque C standard, les cas de double désallocation sont automatiquement détectés. Prenez l'​exemple ci-dessous: 
- 
-<code c dubla_dealocare.c>​ 
-#include <​stdlib.h>​ 
- 
-int main(void) 
-{ 
-char *p; 
- 
-p = malloc(10); 
-free(p); 
-free(p); 
- 
-return 0; 
-} 
-</​code>​ 
- 
-L'​exécution de l'​exécutable obtenu à partir du programme ci-dessus entraîne l'​affichage d'un message de libération double glibc spécifique d'une région de mémoire et l'​arrêt du programme: 
- 
-<code bash> 
- ​so@spook$ make 
- cc -Wall -g    dfree.c ​  -o dfree 
- ​so@spook$ ./​dfree ​ 
-  *** glibc detected *** ./dfree: double free or corruption (fasttop): 0x0000000000601010 *** 
- ​======= Backtrace: ========= 
- /​lib/​libc.so.6[0x2b675fdd502a] 
- /​lib/​libc.so.6(cfree+0x8c)[0x2b675fdd8bbc] 
- ​./​dfree[0x400510] 
- /​lib/​libc.so.6(__libc_start_main+0xf4)[0x2b675fd7f1c4] 
- ​./​dfree[0x400459] 
- 
-</​code>​ 
- 
-Des situations de double transaction sont également détectées par Valgrind. 
- 
-==== Autres utilitaires pour résoudre les problèmes de travail avec la mémoire ==== 
- 
-Les utilitaires présentés ci-dessus ne sont pas les seuls utilisés pour détecter les problèmes dans [[http://​en.wikipedia.org/​wiki/​Category:​Memory_management_software|travailler avec la mémoire]]. Les autres utilitaires sont: 
- 
-  *[[http://​dmalloc.com/​|dmalloc]] 
-  *[[http://​mpatrol.sourceforge.net/​|mpatrol]] 
-  *[[http://​duma.sourceforge.net|DUMA]] 
-  *[[http://​perens.com/​works/​software/​ElectricFence/​|Electric Fence]] 
- 
-===== Mémoire virtuelle ===== 
- 
-Le mécanisme de mémoire virtuelle est utilisé par le cœur du système d'​exploitation pour implémenter une politique de gestion de mémoire efficace. Ainsi, bien que les applications utilisent actuellement la mémoire virtuelle, elles ne le font pas explicitement. Cependant, dans certains cas, les applications utilisent explicitement la mémoire virtuelle. 
- 
-Le système d'​exploitation fournit des primitives pour mapper des fichiers, de la mémoire ou des périphériques à l'​espace d'​adressage d'un processus. 
-  *** Le mappage de fichiers ** en mémoire est utilisé dans certains systèmes d'​exploitation pour implémenter des mécanismes de mémoire partagée. De plus, ce mécanisme permet d'​implémenter la pagination à la demande et des bibliothèques partagées. 
-  *** Le mappage de la mémoire ** dans l'​espace d'​adressage est utile lorsqu'​un processus souhaite allouer une grande quantité de mémoire. 
-  *** Le mappage d'​appareil ** est utile lorsqu'​un processus souhaite utiliser directement la mémoire d'un appareil (comme une carte vidéo). 
- 
-==== Concepts théoriques ====  ​ 
- 
-La taille de l'​espace d'​adressage virtuel d'un processus dépend de la taille des registres du processeur. Ainsi, sur un système 32 bits, un processus pourra accéder à 2 ^ 32 = 4 Go d'​espace mémoire (en revanche, sur un système 64 bits, il accédera théoriquement à 2 ^ 64 B). L'​espace mémoire du processus est divisé en un espace réservé aux adresses de noyau virtuel - cet espace est commun à tous les processus - et l'​espace d'​adressage virtuel (propre) du processus. La plupart du temps, le partage entre les deux est de 3/1 (3 Go d'​espace utilisateur contre 1 Go d'​espace noyau). 
- 
-La mémoire physique (RAM) est répartie entre les processus actuellement actifs et le système d'​exploitation. Ainsi, selon la quantité de mémoire dont nous disposons sur la machine physique, il est possible d'​épuiser toutes les ressources et de ne pas pouvoir démarrer un nouveau processus. Pour éviter ce scénario, le mécanisme de mémoire virtuelle a été introduit. De cette façon, même si l'​espace virtuel (composé du segment de texte, de la date, du tas, de la pile) d'un processus est plus grand que la mémoire physique disponible sur le système, le processus pourra s'​exécuter en mémoire en ne chargeant que les pages dont il a besoin dans temps d'​exécution (paging à la demande). 
- 
-L'​espace d'​adressage virtuel est divisé en // pages virtuelles // (page). Le correspondant de la mémoire physique est // la page physique // (frame). La taille d'une page virtuelle est égale à celle d'une page physique. La taille est donnée par le matériel (dans la plupart des cas, une page a 4 Ko sur un système 32 bits ou 64 bits). 
- 
-Tant qu'un processus en cours d'​exécution n'​accède qu'aux pages résidant en mémoire, il s'​exécute comme s'il avait tout l'​espace mappé dans la mémoire physique. Lorsqu'​un processus souhaite accéder à une certaine page virtuelle, qui n'est pas mappée en mémoire, une // erreur de page // sera générée, et après cette page manquante, la page virtuelle sera mappée sur une page physique. Deux processus différents ont un espace virtuel différent, mais certaines pages virtuelles de ces processus peuvent être mappées sur la même page physique. Ainsi, deux processus différents peuvent partager la même page physique, mais ne partagent pas de pages virtuelles. 
- 
-=== malloc === 
- 
-Comme nous l'​avons trouvé dans la section [[#​Allocation de mémoire sous Linux|Allocation de mémoire sous Linux]], ''​malloc''​ alloue de la mémoire sur le tas, donc dans l'​espace virtuel du processus. ​ 
- 
-L'​allocation de mémoire virtuelle se fait au niveau de la page, de sorte que '​malloc'​ alloue réellement le plus petit nombre de pages virtuelles qui composent l'​espace mémoire requis. Soit le code suivant:<​code c> 
-char *p = malloc(4150);​ 
-DIE(p == NULL, "​malloc failed"​);​ 
-</​code>​ 
- 
-Étant donné qu'une page virtuelle a 4KB = 4096 octets, alors l'​appel ''​ malloc ''​ allouera 4096 octets + 54 octets = 4KB + 54 octets, espace qui n'est pas contenu dans une seule page virtuelle, de sorte que 2 pages seront allouées virtuel. Au moment de l'​affectation avec "​malloc"​ il n'y aura pas de pages allouées (tout le temps) et physiques; ils ne seront alloués que lors de l'​accès aux données de la zone de mémoire allouée avec '​malloc'​. Par exemple, lors de l'​accès à un élément à partir de p, une // erreur de page // sera générée et la page virtuelle contenant cet élément sera mappée sur une page physique. 
- 
-<​note>​ 
-Généralement,​ sur le petit appel malloc (lorsque l'​appel système ''​ brk ''​ est rappelé), la bibliothèque C standard analyse les pages allouées, génère des erreurs de page et lorsque l'​appel revient, les pages physiques seront déjà allouées. On peut dire que pour les petites dimensions, l'​appel '​malloc',​ vu depuis l'​application (en dehors de la bibliothèque standard C), alloue également des pages physiques et des pages virtuelles. 
- 
-De plus, l'​allocation réelle des pages virtuelles et physiques se produit au moment de l'​appel système ''​ brk ''​. Il alloue un espace plus grand, et les futurs appels ''​malloc''​ utiliseront cet espace. De cette façon, les appels "​malloc"​ suivants seront efficaces: ils ne feront pas d'​appels système, ils ne feront pas d'​allocation efficace d'​espace virtuel ou physique, ils ne généreront pas de // défauts de page //. 
- 
-L'​appel malloc est plus efficace que l'​appel calloc car il ne parcourt pas l'​espace alloué pour le remplir de zéros. Cela signifie que ''​malloc''​ renverra la zone allouée avec les informations qui s'y trouvent; Dans certaines situations, cela peut constituer un risque pour la sécurité - si les données y sont privées. 
-</​note>​ 
- 
-==== Mappage de fichiers ==== 
- 
-Après avoir mappé un fichier à l'​espace d'​adressage du processus, l'​accès à ce fichier peut se faire de la même manière que l'​accès aux données à partir d'un vecteur. L'​efficacité de la méthode vient du fait que la zone mémoire est gérée de la même manière que la mémoire virtuelle, soumise aux règles d'​évacuation du disque lorsque la mémoire devient insuffisante (de cette façon on peut travailler avec des mappages qui dépassent la taille effective de la mémoire physique). De plus, la partie ''​ I / O ''​ est réalisée par le noyau, le programmeur écrivant du code qui ne récupère / stocke que les valeurs de / dans la région mappée. Ainsi, il n'est plus appelé ''​ read '',​ ''​ write '',​ ''​ lseek ''​ - ce qui simplifie souvent l'​écriture de code. 
- 
-<note important>​ Tous les descripteurs de fichiers ne peuvent pas être mappés en mémoire. Les sockets, les tuyaux, les périphériques qui n'​autorisent que l'​accès séquentiel (par exemple, le périphérique char) sont incompatibles avec les concepts de mappage. Il existe des cas où les fichiers normaux ne peuvent pas être mappés (par exemple, s'ils n'​étaient pas ouverts pour plus de lisibilité;​ pour plus d'​informations:​ ** man mmap **). </​note>​ 
- 
-=== mmap === 
- 
-Prototype de fonction [[http://​linux.die.net/​man/​2/​mmap|mmap]] qui permet le mappage d'un fichier à l'​espace d'​adressage du processus est le suivant: 
- 
-<code cpp> 
-void *mmap(void *start, size_t length, int prot, int flags, int fd, off_t offset); 
-</​code>​ 
- 
-La fonction retournera en cas d'​erreur ''​ MAP_FAILED ''​. Si le mappage a réussi, il renverra un pointeur vers une zone de mémoire dans l'​espace d'​adressage du processus, la zone où le fichier décrit par le descripteur ''​ fd ''​ a été mappé, en commençant par l'​offset. L'​utilisation du paramètre ''​ start ''​ vous permet de proposer une zone mémoire spécifique à mapper. L'​utilisation de la valeur ''​ NULL ''​ pour le paramètre ''​ start ''​ indique l'​absence de préférence concernant la zone dans laquelle l'​allocation sera effectuée. L'​adresse spécifiée par le paramètre ''​ start ''​ doit être multiple de // la taille d'une page //. Si le système d'​exploitation ne peut pas mapper le fichier à l'​adresse requise, il le mappera à un emplacement proche et multiple de la taille d'une page. L'​adresse la plus appropriée est également retournée. 
- 
-Le paramètre ''​ prot ''​ spécifie le type d'​accès souhaité: 
-  *''​ PROT_READ ''​ (lire) 
-  *''​ PROT_WRITE ''​ (écrire) 
-  *''​ PROT_EXEC ''​ (exécution) 
-  *''​ PROT_NONE ''​. 
-Lorsque la zone est utilisée autrement que ce qui est indiqué, un signal ''​ SIGSEGV ''​ est généré. 
- 
-Le paramètre ''​flags''​ vous permet de définir le type de mappage que vous souhaitez; il peut prendre les valeurs suivantes (combinées par OR en bits; il doit y avoir au moins ''​ MAP_PRIVATE ''​ ou ''​ MAP_SHARED ''​):​ 
-  *''​ MAP_PRIVATE ''​ - Utilisez une politique // de copie sur écriture //. La zone contiendra initialement une copie du fichier, mais les écritures ne sont pas effectuées dans le fichier. Les modifications // ne seront pas visibles dans les autres processus //, s'il y a plusieurs processus qui ont fait "​mmap"​ sur la même zone du même fichier. 
-  *''​ MAP_SHARED ''​ - Les entrées sont mises à jour immédiatement dans tous les mappages existants. De cette façon, tous les processus qui ont effectué des mappages verront les modifications. En effet, les mappages "​MAP_SHARED"​ sont effectués sur les pages physiques du cache de pages et les appels r / w utilisent les pages physiques du cache de pages pour réduire le nombre de lectures / écritures à partir du disque. Les mises à jour du disque auront lieu ultérieurement,​ sans précision. 
-  * ''​ MAP_FIXED ''​ - Si l'​affectation à l'​adresse spécifiée par ''​ start ''​ ne peut pas être effectuée, l'​appel échouera. 
-  * ''​ MAP_LOCKED ''​ - Cette page sera bloquée de cette manière [[http://​linux.die.net/​man/​2/​mlock|mlock]]. 
-  * ''​ MAP_ANONYMOUS ''​ - Mappe la RAM (les arguments ''​ fd ''​ et ''​ offset ''​ sont ignorés). 
- 
-  Il est à noter que l'​utilisation de ''​ MAP_SHARED ''​ permet le partage de mémoire entre des processus qui ne sont pas liés. Dans ce cas, le contenu du fichier devient le contenu initial de la mémoire partagée et toutes les modifications apportées par les processus dans cette zone sont ensuite copiées dans le fichier, garantissant ainsi la persistance dans le système de fichiers. 
- 
-=== msync === 
- 
-La fonctionnalité est disponible pour déclencher explicitement la synchronisation des fichiers avec le mappage de la mémoire [[http://​linux.die.net/​man/​2/​msync|msync]]:​ 
- 
-<code c> 
-int msync(void *start, size_t length, int flags); 
-</​code>​ 
- 
-où ''​ drapeaux ''​ peut être: 
-  * ''​ MS_SYNC ''​ - Les données seront écrites dans le fichier et attendront jusqu'​à ce qu'​elles soient terminées. 
-  * ''​ MS_ASYNC ''​ - La séquence de sauvegarde est lancée, mais son achèvement n'est pas prévu. 
-  * ''​ MS_INVALIDATE ''​ - Le mappage de la zone à partir d'​autres processus est invalidé, de sorte que les processus seront mis à jour avec les nouvelles données entrées. 
- 
-L'​appel msync est utile pour écrire des pages modifiées du cache de pages sur le disque, afin d'​éviter de perdre des modifications en cas de plantage du système. 
- 
-==== Allouer de la mémoire dans l'​espace d'​adressage du processus ==== 
- 
-Sous UNIX, traditionnellement,​ pour l'​allocation de // mémoire dynamique //, l'​appel système est utilisé [[http://​linux.die.net/​man/​2/​brk|brk]]. Cet appel augmente ou diminue la zone de segment de mémoire associée au processus. Avec la fourniture d'​appels du système de gestion de la mémoire virtuelle aux applications ([[http://​linux.die.net/​man/​2/​mmap|mmap]]),​ il était possible pour les processus d'​allouer de la mémoire à l'aide de ces nouveaux appels système. Fondamentalement,​ les processus peuvent mapper la mémoire à l'​espace d'​adressage,​ pas aux fichiers. 
- 
-Les processus peuvent nécessiter l'​allocation d'une zone mémoire à partir d'une adresse spécifique dans l'​espace adresse, même avec une certaine stratégie d'​accès (lecture, écriture ou exécution). Sous UNIX, cela se fait également via la fonction ​ [[http://​linux.die.net/​man/​2/​mmap|mmap]]. Pour cela, le paramètre ''​ flags ''​ doit contenir le flag ''​ MAP_ANONYMOUS ''​. 
- 
-==== Appareils de cartographie ==== 
- 
-Il est même possible pour les applications de mapper un périphérique d'​entrée-sortie dans l'​espace d'​adressage du processus. Ceci est utile, par exemple, pour les cartes vidéo: une application peut mapper la mémoire physique de la carte vidéo à l'​espace d'​adressage. Sous UNIX, les périphériques étant représentés par des fichiers, il suffit pour cela d'​ouvrir le fichier associé au périphérique et de l'​utiliser dans un appel ''​ mmap ''​. 
- 
-<​note>​ Tous les appareils ne peuvent pas être mappés en mémoire, mais lorsqu'​ils sont mappés, l'​importance de ce mappage dépend strictement de l'​appareil. </​note>​ 
- 
-Un autre exemple d'un périphérique qui peut être mappé est la mémoire elle-même. Sous Linux, vous pouvez utiliser le fichier ''​ / dev / zero ''​ pour effectuer le mappage de la mémoire, comme si vous utilisiez l'​indicateur ''​ MAP_ANONYMOUS ''​. 
- 
-==== Suppression d'une zone de l'​espace d'​adressage ==== 
- 
-Si vous souhaitez effacer une zone de l'​espace d'​adressage du processus, vous pouvez utiliser la fonction [[http://​linux.die.net/​man/​3/​munmap|munmap]]:​ 
- 
-<code c> 
- int munmap(void *start, size_t length); 
-</​code>​ 
- 
-''​ start ''​ est l'​adresse de la première page à délimiter (doit être multiple de // la taille d'une page //). Si la ''​longueur''​ n'est pas une taille qui représente un nombre entier de pages, elle sera arrondie plus haut. La zone peut contenir des pièces qui ont déjà été supprimées. Vous pouvez ainsi effacer plusieurs zones en même temps. 
- 
-==== Redimensionner une zone mappée ==== 
- 
-La fonction peut être utilisée pour effectuer des opérations de redimensionnement de la zone de mappage [[http://​linux.die.net/​man/​2/​mremap|mremap]]:​ 
- 
-<code c> 
-void *mremap(void *old_address,​ size_t old_size, size_t new_size, unsigned long flags); 
-</​code>​ 
- 
-La zone décrite par ''​ old_address ''​ et ''​ old_size ''​ doit appartenir à un seul mappage. Une seule option est disponible pour les ''​ indicateurs '':​ ''​ MREMAP_MAYMOVE ''​ qui montre qu'il est correct d'​obtenir le nouveau mappage pour effectuer un nouveau mappage dans une autre zone de mémoire (l'​ancienne zone étant floue). 
- 
-==== Modifier la protection d'une zone cartographiée ==== 
- 
-Parfois, il est nécessaire de changer la façon (droits d'​accès) dans laquelle une zone a été cartographiée. Pour cela, vous pouvez utiliser la fonction ​ [[http://​linux.die.net/​man/​2/​mprotect|mprotect]]:​ 
- 
-<code c> 
-int mprotect(const void *addr, size_t len, int prot); 
-</​code>​ 
- 
-La fonction reçoit comme paramètres la plage d'​adresses [''​ addr '',​ ''​ addr ''​ + ''​ len ''​ - 1] et de nouveaux droits d'​accès (''​ PROT_READ '',​ ''​ PROT_WRITE '',​ ''​ PROT_EXEC '',''​PROT_NONE''​). Comme dans [[http://​linux.die.net/​man/​2/​munmap|munmap]],​ ''​ addr ''​ doit être multiple de // la taille de la page //. La fonction modifiera la protection de toutes les pages contenant au moins un octet dans la plage spécifiée. 
- 
-==== Exemple ==== 
-<code cpp> 
-int fd = open("​fisier",​ O_RDWR); 
-void *p = mmap(NULL, 2*getpagesize(),​ PROT_NONE, MAP_SHARED, fd, 0); 
-// *(char*)p = '​a';​ // segv fault 
-mprotect(p, 2*getpagesize(),​ PROT_WRITE);​ 
-*(char*)p = '​a';​ 
-munmap(p, 2*getpagesize());​ 
-</​code>​ 
- 
-L'​appel getpagesize renverra la taille d'une page en octets. 
- 
-====== Exercices ====== 
- 
-==== Exercice 1 - Zones de stockage variables ==== 
- 
-Entrez dans le répertoire ''​ 1 compteur ''​ et analysez la fonction ''​ inc '',​ qui renvoie un nombre entier représentant le nombre d'​appels jusqu'​à présent. Compilez le programme en exécutant la commande ''​ make ''​ et inspectez où la mémoire de la variable ''​ cnt ''​ a été allouée. 
- 
-Utilisez les commandes suivantes: 
-<code bash> 
-make 
-objdump -t counter | grep cnt 
-</​code>​ 
- 
-==== Exercice 2 - L'​espace d'​adressage d'un processus ==== 
-  
-Entrez dans le répertoire ''​ 2-adr_space ''​ et ouvrez la source ''​ adr_space.c ''​. Dans un autre terminal, compilez et exécutez le programme. Notez les zones de mémoire dans l'​exécutable où les variables sont enregistrées,​ à l'aide de la commande: <code bash> 
-objdump -t adr_space | grep var 
-</​code>​ 
- 
-<note tip> 
-Notez que certaines variables apparaissent dans la table des symboles (variables locales globales et statiques - comme indiqué par les drapeaux ''​ g ''​ (global) et ''​ l ''​ (local) à côté d'​elles;​ ''​ man objdump ''​ pour plus d'​informations) et d'​autres non. Les variables qui n'​apparaissent pas dans le tableau sont sur la pile. 
-</​note>​ 
- 
-Afficher le contenu de la zone ''​ .data ''​ à l'aide de l'​utilitaire ''​ readelf ''​ 
- 
-<note tip> 
-** Astuce **: Vous devez afficher le vidage hexadécimal de la section ''​ .data ''​ de l'​exécutable ''​ adr_space ''​. Voir la page de manuel '​readelf'​ pour le paramètre correct. 
- 
-N'​oubliez pas d'​ajouter le nom du fichier exécutable en tant que paramètre de la commande '​readelf'​. 
-</​note>​ 
- 
-==== Exercice 3 - Allocation et utilisation de la mémoire ==== 
-  
-Entrez dans le répertoire ''​ 3-alloc ''​ et ouvrez la source ''​ alloc.c ''​. Dans le même temps, décompressez l'une des lignes commençant par ''​ TODO x ''​. 
-<note warning> 
-Une seule des 3 lignes doit être mise hors service avant de compiler le programme. 
-</​note>​ 
-Dans un autre terminal, compilez le programme. Notez les zones de mémoire de l'​exécutable dans lesquelles les variables et leur taille sont enregistrées,​ puis affichez la taille de l'​exécutable à l'aide des commandes: <code bash> 
-make 
-objdump -t alloc | grep var 
-ls -lh alloc 
-</​code>​ 
- 
-==== Exercice 4 - Enquêter sur les mappages à l'aide de pmap ==== 
- 
-Entrez dans le répertoire ''​ 4-intro ''​ et compilez la source ''​ intro.c ''​. Exécutez le programme ''​ intro '':<​code>​ 
-./intro 
-</​code>​ 
- 
-Dans une autre console, utilisez la commande [[http://​linux.die.net/​man/​1/​pmap | pmap]].:<​code bash> watch -d pmap $(pidof intro) 
-</​code>​ pour suivre les modifications apportées à la mémoire de processus. 
- 
-Dans la première console, utilisez ''​ ENTER ''​ pour continuer le programme. Dans la deuxième console, suivez les modifications qui se produisent après les différents types de mappage dans le code. 
- 
-Analysez les mappages effectués par le processus init à l'aide de la commande: <code bash>​sudo pmap 1 
-</​code>​ 
- 
-<note tip> 
-Vous pouvez voir que pour les bibliothèques partagées (par exemple, ''​ libc ''​),​ trois zones sont mappées: la zone de code (lecture-exécution),​ la zone .rodata (lecture seule) et la zone .data (lecture-écriture). 
-</​note>​ 
- 
-==== Exercice 5 - Écrire dans un fichier - écrire ou mmap==== 
- 
-Entrez dans le répertoire ''​ 6-compare ''​ et inspectez les sources ''​ write.c ''​ et ''​ mmap.c '',​ puis compilez. Obtenez le runtime des deux programmes en utilisant la commande '​time':​ <code bash> ​ 
-time ./write; time ./mmap 
-</​code>​ 
- 
-Notez que la variante "​mmap"​ est plus rapide que la ""​ variante d'​écriture. Nous utiliserons [[http://​linux.die.net/​man/​1/​strace|strace]] pour voir quels appels système sont effectués pour exécuter chaque programme: 
-<code bash> 
-strace -c ./write 
-strace -c ./mmap 
-</​code>​ 
- 
-A partir de la sortie ""​ strace "",​ nous remarquons que le programme ""​ write ""​ fait de nombreux (100000) appels ""​ write ""​ et à cause de cela est plus lent que le programme "​mmap"​. 
- 
-Ensuite, nous examinerons les deux modes de mappage de fichiers: ''​ MAP_SHARED ''​ et ''​ MAP_PRIVATE ''​. Notez que le fichier ''​ test_mmap ''​ (créé par le programme ''​ mmap ''​ avec ''​ MAP_SHARED ''​) contient 100000 lignes: 
-<code bash> 
-cat test_mmap | wc -l 
-</​code>​ 
- 
-Dans le programme ''​ mmap.c '',​ changez l'​indicateur de création de mémoire partagée de ''​ MAP_SHARED ''​ en ''​ MAP_PRIVATE '',​ compilez et réexécutez:​ 
-<code bash> 
-./mmap 
-cat test_mmap | wc -l 
-</​code>​ 
- 
-<note tip>Les modifications apportées à une zone de mémoire mappée avec ''​ MAP_PRIVATE ''​ ne seront pas visibles par les autres processus et n'​atteindront pas le fichier mappé sur le disque.</​note>​ 
- 
-==== Exercice 6 - Défauts de page ==== 
- 
-Entrez dans le répertoire '​7-faults'​ et parcourez le contenu du fichier fork-faults.c. 
- 
-Nous utiliserons l'​utilitaire ''​ pidstat ''​ ([[http://​www.cyberciti.biz/​open-source/​command-line-hacks/​linux-monitor-process-using-pidstat| pidstat]]) à partir du package ''​ sysstat ''​ pour surveiller les défauts de page causés par un essai. 
- 
-<note important>​Si vous rencontrez des problèmes lors de l'​installation du package ''​ sysstat '',​ téléchargez-le depuis [[http://​ro.archive.ubuntu.com/​ubuntu/​pool/​main/​s/​sysstat/​sysstat_11.2.0-1_i386.deb|ici]] et installez-le en utilisant la commande ''​dpkg''​. 
-<​code>​ 
-student@spook:​~$ wget http://​ro.archive.ubuntu.com/​ubuntu/​pool/​main/​s/​sysstat/​sysstat_11.2.0-1_i386.deb 
-student@spook:​~$ sudo dpkg -i sysstat_11.2.0-1_i386.deb ​ 
-</​code>​ 
-</​note>​ 
- 
-Exécutez le programme '​fork-faults'​. Dans une autre console, exécutez la commande 
- 
-<code bash>​pidstat -r -T ALL -p $(pidof fork-faults) 5</​code>​ 
- 
-pour suivre les défauts de page. La commande ci-dessus vous montre un message toutes les 5 secondes; nous nous intéressons aux valeurs ''​ minflt-nr ''​. 
- 
-À son tour, appuyez sur la touche ENTRÉE de la console sur laquelle vous avez exécuté le programme fork et voyez la sortie de la commande pidstat. Suivez l'​évolution du nombre de défauts de page pour les deux essais: parent et enfant. Les défauts de page qui se produisent dans le cas d'une copie sur écriture dans le processus enfant seront visibles plus tard dans le processus parent (une fois que le processus enfant aura terminé son exécution). 
- 
-<​note>​Le package ''​ sysstat ''​ contient également l'​utilitaire ''​ jump ''​ où vous pouvez collecter et faire des rapports sur l'​activité du système. Pour activer l'​enregistrement des données, l'​indicateur ''​ ENABLED ''​ dans ''​ / etc / default / sysstat ''​ doit être défini. 
-Avec l'​utilitaire "​jump",​ vous pouvez surveiller des informations telles que le chargement du processeur, l'​utilisation de la mémoire et des pages, les opérations d'E / S, l'​activité du processus. Détails que vous pouvez trouver auprès de [[http://​www.cyberciti.biz/​tips/​identifying-linux-bottlenecks-sar-graphs-with-ksar.html| sar]]. </​note>​ 
- 
- 
- 
  
CC Attribution-Share Alike 3.0 Unported
www.chimeric.de Valid CSS Driven by DokuWiki do yourself a favour and use a real browser - get firefox!! Recent changes RSS feed Valid XHTML 1.0