Les systèmes d'exploitation modernes adoptent généralement un mécanisme de gestion de la mémoire virtuelle (Virtual Memory Management), qui nécessite la prise en charge de la MMU (Memory Management Unit) dans le processeur. Tout d’abord, les concepts de PA et VA sont introduits.
Si le processeur n'a pas de MMU, ou s'il existe une MMU mais qu'elle n'est pas activée, l'adresse mémoire émise par le L'unité d'exécution du processeur sera directement transférée à la broche du moteur de la puce, elle est reçue par la puce mémoire (ci-après dénommée mémoire physique pour la distinguer de la mémoire virtuelle), qui est appelée PA (adresse physique, ci-après dénommée PA). , comme le montre la figure ci-dessous.
Si la MMU est activée sur le processeur, le processeur exécute L'adresse mémoire envoyée par l'unité sera interceptée par la MMU. L'adresse de la CPU à la MMU est appelée adresse virtuelle (ci-après dénommée VA), et la MMU traduit cette adresse en une autre adresse et l'envoie au. broche d'adresse externe de la puce CPU, c'est-à-dire que VA est mappé à PA, comme le montre la figure ci-dessous.
S'il s'agit d'un processeur 32 bits, le bus d'adresse interne est de 32 bits et connecté à l'unité d'exécution du CPU (seulement 4 lignes d'adresse sont schématiquement dessinées sur la figure), tandis que le bus d'adresse externe après conversion MMU ne l'est pas. Il doit être en 32 bits. En d'autres termes, l'espace d'adressage virtuel et l'espace d'adressage physique sont indépendants. L'espace d'adressage virtuel du processeur 32 bits est de 4 Go, tandis que l'espace d'adressage physique peut être supérieur ou inférieur à 4 Go.
La MMU mappe VA à PA en unités de pages (Page) La taille de page des processeurs 32 bits est généralement de 4 Ko. Par exemple, la MMU peut mapper une page de 0xb7001000~0xb7001fff dans VA à une page de 0x2000~0x2fff dans PA via un élément de mappage. Si l'unité d'exécution du processeur souhaite accéder à l'adresse virtuelle 0xb7001008, l'adresse physique réelle accessible est 0x2008. Les pages de la mémoire physique sont appelées pages physiques ou cadres de page. Quelle page de mémoire virtuelle est mappée à quel cadre de page de mémoire physique est décrit par la table de pages (Table de pages). La table de pages est stockée dans la mémoire physique. La MMU recherchera la table de pages pour déterminer quel PA un VA doit utiliser. être mappé à.
L'espace d'adressage virtuel de la plate-forme x86 est 0x0000 0000~0xffff ffff. De manière générale, les 3 premiers Go (0x0000 0000~0xbfff ffff) sont l'espace utilisateur et le dernier 1 Go (0xc000 0000~0xffff ffff) est l'espace noyau.
Segment de texte, y compris le segment .text, le segment .rodata, le segment .plt, etc. Il est chargé en mémoire depuis /bin/bash et l'autorisation d'accès est r-x.
Segment de données, y compris le segment .data, le segment .bss, etc. Il est également chargé en mémoire depuis /bin/bash et l'autorisation d'accès est rw-.
Tas : Le tas est simplement l'espace restant dans la mémoire de l'ordinateur, malloc Fonctionne dynamiquement la mémoire allouée est allouée ici. Lors de l'allocation dynamique de mémoire, l'espace du tas peut croître vers des adresses plus élevées. La limite supérieure de l'adresse de l'espace du tas est appelée Break. Pour augmenter l'espace du tas jusqu'à une adresse élevée, le Break doit être augmenté pour mapper les nouvelles pages de mémoire virtuelle à la mémoire physique. Ceci est réalisé via l'appel système brk. La fonction appelle également brk pour demander une allocation à la mémoire du noyau.
Pile : La pile est une zone mémoire spécifique, dans laquelle la partie haute adresse stocke les variables d'environnement et les paramètres de ligne de commande du processus, et la partie basse adresse La partie -address stocke les variables d'environnement et les paramètres de ligne de commande du processus. Enregistrez partiellement les cadres de pile de fonctions, l'espace de pile augmente vers des adresses inférieures, mais il n'y a évidemment pas autant de place pour la croissance que l'espace du tas, car ce n'est pas rare. les applications réelles allouent dynamiquement de grandes quantités de mémoire, mais il existe des dizaines de couches profondes. Il est très rare que les appels de fonction aient de nombreuses variables locales à chaque niveau de l'appel.
Si vous ne faites pas attention à l'allocation de mémoire lors de l'écriture d'un programme, les problèmes suivants peuvent survenir dans le tas et la pile :
Fuite de mémoire : Si vous demandez un espace dans le tas via malloc dans une fonction et déclarez une variable de pointeur sur la pile pour la sauvegarder, alors lorsque la fonction se termine, les variables membres de la fonction seront libérées , y compris cette variable de pointeur, alors cet espace est introuvable et ne peut pas être libéré. Au fil du temps, cela peut provoquer les problèmes de fuite de mémoire suivants.
Débordement de pile : Si vous mettez trop de données sur la pile (comme de grandes structures et tableaux), cela peut provoquer un "débordement de pile" ( Stack Overflow) question, le programme se terminera également. Pour éviter ce problème, malloc doit être utilisé pour demander de l'espace de tas lors de la déclaration de telles variables.
Pointeur sauvage et Défaut de segmentation : Si l'espace pointé par un pointeur a été libéré, puis essayez d'utiliser le pointeur pour accéder l'espace déjà libéré provoquera des problèmes de "Segment Fault". À ce stade, le pointeur est devenu un pointeur sauvage et le pointeur sauvage doit être effacé manuellement à temps.
La gestion de la mémoire virtuelle peut contrôler les droits d'accès à la mémoire physique. La mémoire physique elle-même ne restreint pas l'accès et n'importe quelle adresse peut être lue et écrite. Cependant, le système d'exploitation nécessite que différentes pages aient des droits d'accès différents. Ceci est obtenu en utilisant le mécanisme de protection de la mémoire du mode CPU et MMU.
La fonction principale de la gestion de la mémoire virtuelle est de permettre à chaque processus d'avoir un espace d'adressage indépendant. Le soi-disant espace d'adressage indépendant signifie que le même VA dans différents processus est mappé à différents PA par MMU, et l'accès à n'importe quelle adresse dans un certain processus ne peut pas accéder aux données d'un autre processus. que tout accès illégal à la mémoire causé par l'exécution d'instructions erronées ou de code malveillant par un processus ne réécrira pas accidentellement les données d'autres processus et n'affectera pas le fonctionnement d'autres processus, garantissant ainsi la stabilité de l'ensemble du système. D'un autre côté, chaque processus pense qu'il possède exclusivement l'intégralité de l'espace d'adressage virtuel, de sorte que la mise en œuvre de l'éditeur de liens et du chargeur sera plus facile sans avoir à se demander si les plages d'adresses de chaque processus sont en conflit.
Le mappage VA vers PA allouera et libérera de la mémoire, ce qui apportera de la commodité, plusieurs blocs de mémoire avec des adresses physiques discontinues peuvent être mappés en un bloc de mémoire avec des adresses virtuelles continues. Par exemple, si vous souhaitez utiliser malloc pour allouer un grand espace mémoire, bien qu'il y ait suffisamment de mémoire physique libre, il n'y a pas suffisamment de mémoire libre continue. Dans ce cas, vous pouvez allouer plusieurs pages physiques discontinues et les mapper à une page virtuelle continue. plage d'adresses.
Si un système exécute plusieurs processus en même temps, la somme de la mémoire allouée à chaque processus peut être supérieure à la mémoire physique réellement disponible. La gestion de la mémoire virtuelle permet à chaque processus de continuer à fonctionner normalement. dans ce cas. Étant donné que chaque processus se voit uniquement attribuer des pages de mémoire virtuelle, les données de ces pages peuvent être mappées sur des pages physiques ou peuvent être temporairement enregistrées sur le disque sans occuper les pages physiques. Le stockage temporaire des pages de mémoire virtuelle sur le disque peut être un disque. partition., ou il peut s'agir d'un fichier disque, appelé périphérique d'échange. Lorsque la mémoire physique n'est pas suffisante, les données de certaines pages physiques rarement utilisées sont temporairement enregistrées sur le périphérique d'échange Ensuite, la page physique est considérée comme libre et peut être réaffectée au processus. Ce processus est appelé Page. dehors. Si le processus doit utiliser la page qui a été échangée, elle sera rechargée dans la mémoire physique à partir du périphérique d'échange. C'est ce qu'on appelle l'échange dans (Page in). Les opérations d'échange et d'échange sont collectivement appelées pagination, donc :
Comme le montre l'image ci-dessous. La première image consiste à échanger, à enregistrer les données de la page physique sur le disque, à démapper l'adresse et à libérer la page physique. La deuxième image consiste à échanger, à allouer une page physique libre, à recharger la page temporaire du disque en mémoire et à établir un mappage d'adresses.
La fonction de bibliothèque standard C malloc peut allouer dynamiquement de la mémoire dans l'espace du tas, son sous-jacent Demandez de la mémoire au système d'exploitation via l'appel système brk. Une fois la mémoire allouée dynamiquement épuisée, elle peut être libérée gratuitement, ou plus précisément, renvoyée à malloc, afin que la mémoire puisse être à nouveau allouée la prochaine fois que malloc est appelé.
1 #include <stdlib.h>2 void *malloc(size_t size); //返回值:成功返回所分配内存空间的首地址,出错返回NULL3 void free(void *ptr);</stdlib.h>
malloc的参数size表示要分配的字节数,如果分配失败(可能是由于系统内存耗尽)则返回NULL。由于malloc函数不知道用户拿到这块内存要存放什么类型的数据,所以返回通用指针void *,用户程序可以转换成其它类型的指针再访问这块内存。malloc函数保证它返回的指针所指向的地址满足系统的对齐要求,例如在32位平台上返回的指针一定对齐到4字节边界,以保证用户程序把它转换成任何类型的指针都能用。
动态分配的内存用完之后可以用free释放掉,传给free的参数正是先前malloc返回的内存块首地址。
举例如下:
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <string.h> 4 typedef struct { 5 int number; 6 char *msg; 7 } unit_t; 8 int main(void) 9 {10 unit_t *p = malloc(sizeof(unit_t));11 if (p == NULL) {12 printf("out of memory\n");13 exit(1);14 }15 p->number = 3;16 p->msg = malloc(20);17 strcpy(p->msg, "Hello world!");18 printf("number: %d\nmsg: %s\n", p->number, p->msg);19 free(p->msg);20 free(p);21 p = NULL;22 return 0;23 }</string.h></stdlib.h></stdio.h>
unit_t *p = malloc(sizeof(unit_t));
这一句,等号右边是void *
类型,等号左边是unit_t *
类型,编译器会做隐式类型转换,我们讲过void *
类型和任何指针类型之间可以相互隐式转换。
虽然内存耗尽是很不常见的错误,但写程序要规范,malloc之后应该判断是否成功。以后要学习的大部分系统函数都有成功的返回值和失败的返回值,每次调用系统函数都应该判断是否成功。
free(p);
之后,p所指的内存空间是归还了,但是p的值并没有变,因为从free的函数接口来看根本就没法改变p的值,p现在指向的内存空间已经不属于用户,换句话说,p成了野指针,为避免出现野指针,我们应该在free(p);
之后手动置p = NULL;
。
应该先free(p->msg)
,再free(p)
。如果先free(p)
,p成了野指针,就不能再通过p->msg
访问内存了。
如果一个程序长年累月运行(例如网络服务器程序),并且在循环或递归中调用malloc分配内存,则必须有free与之配对,分配一次就要释放一次,否则每次循环都分配内存,分配完了又不释放,就会慢慢耗尽系统内存,这种错误称为内存泄漏(Memory Leak)。另外,malloc返回的指针一定要保存好,只有把它传给free才能释放这块内存,如果这个指针丢失了,就没有办法free这块内存了,也会造成内存泄漏。例如:
1 void foo(void)2 {3 char *p = malloc(10);4 ...5 }
Lorsque la fonction foo revient, elle doit libérer l'espace mémoire de la variable locale p. L'adresse mémoire vers laquelle elle pointe est perdue, et ces 10 octets ne peuvent pas être libérés. Les bogues de fuite de mémoire sont difficiles à trouver car ils ne provoquent pas d'erreurs d'exécution du programme comme un accès hors limites. Une petite quantité de fuites de mémoire n'affecte pas le bon fonctionnement du programme. Une grande quantité de fuites de mémoire entraînera une pénurie de mémoire. mémoire système, entraînant des changements de page fréquents, ce qui non seulement affecte le processus en cours, mais ralentit l'ensemble du système.
Il existe quelques cas particuliers concernant malloc et free. L'appel à malloc(0) est également légal et renverra un pointeur non NULL. Ce pointeur peut également être transmis à free for release, mais la mémoire n'est pas accessible via ce pointeur. free(NULL) est également légal et ne fait rien, mais libérer un pointeur sauvage est illégal. Par exemple, appeler d'abord malloc pour renvoyer un pointeur p, puis appeler free(p); générer une erreur d'exécution.
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!