Dans le système d'exploitation Linux, l'intérieur de l'espace d'adressage virtuel est divisé en deux parties : l'espace noyau et l'espace utilisateur Les systèmes avec des chiffres différents ont des plages d'espace d'adressage différentes. Par exemple, les systèmes 32 bits et 64 bits les plus courants sont les suivants :
Vous pouvez le voir ici :
Parlons de la différence entre l'espace noyau et l'espace utilisateur :
Bien que chaque processus possède sa propre mémoire virtuelle indépendante, L'adresse du noyau dans chaque mémoire virtuelle est en fait associée à la même mémoire physique. De cette façon, une fois que le processus est passé à l'état du noyau, il peut facilement accéder à l'espace mémoire du noyau.
Ensuite, apprenons-en davantage sur la division de l'espace virtuel. L'espace utilisateur et l'espace noyau sont divisés de différentes manières. Je ne dirai pas grand-chose sur la répartition de l'espace noyau.
Jetons un coup d'œil à la répartition de l'espace utilisateur. En prenant comme exemple un système 32 bits, j'ai dessiné une image pour montrer leur relation :
Vous pouvez voir sur cette image que la mémoire de l'espace utilisateur est divisée en 6 segments de mémoire différents de bas à haut :
«
Dans quelles circonstances malloc() alloue-t-il de la mémoire via brk() ? Dans quel scénario la mémoire est-elle allouée via mmap() ?
”
malloc() a un seuil défini par défaut dans le code source :
Non,
malloc() alloue de la mémoire virtuelle.
Si la mémoire virtuelle allouée n'est pas accédée, la mémoire virtuelle ne sera pas mappée à la mémoire physique, elle n'occupera donc pas la mémoire physique. Seulement lors de l'accès à l'espace d'adressage virtuel alloué, le système d'exploitation recherchera la table des pages et constatera que la page correspondant à la mémoire virtuelle n'est pas dans la mémoire physique, il déclenchera une interruption de défaut de page, puis le système d'exploitation établir un lien entre la mémoire virtuelle et la relation de mappage de la mémoire physique entre.
Combien de mémoire virtuelle malloc(1) allouera-t-il ? LorsqueLa quantité spécifique d'espace qui sera pré-alloué est liée au gestionnaire de mémoire utilisé par malloc Nous utiliserons le gestionnaire de mémoire par défaut de malloc (Ptmalloc2) pour analyser. Ensuite, faisons une expérience et utilisons le code suivant pour voir quelle quantité d'espace mémoire est réellement allouée par le système d'exploitation lors de la demande d'1 octet de mémoire via malloc.
#include #include int main() { printf("使用cat /proc/%d/maps查看内存分配\n",getpid()); //申请1字节的内存 void *addr = malloc(1); printf("此1字节的内存起始地址:%x\n", addr); printf("使用cat /proc/%d/maps查看内存分配\n",getpid()); //将程序阻塞,当输入任意字符时才往下执行 getchar(); //释放内存 free(addr); printf("释放了1字节的内存,但heap堆并不会释放\n"); getchar(); return 0; }
Exécutez le code (
Je m'explique à l'avance, la version de la bibliothèque glibc que j'utilise est la 2.17) :
Nous pouvons visualiser la distribution de mémoire du processus via le fichier /proc//maps. Je filtre la plage d'adresses mémoire dans le fichier de cartes par cette adresse de début de mémoire de 1 octet.
[root@xiaolin ~]# cat /proc/3191/maps | grep d730 00d73000-00d94000 rw-p 00000000 00:00 0 [heap]
Vous pouvez voir que la plage d'adresses mémoire de l'espace du tas est 00d73000-00d94000 et que la taille de cette plage est de 132 Ko, ce qui signifie que
malloc(1) pré-alloue en fait 132 Ko de mémoire.
Certains étudiants ont peut-être remarqué que l'adresse de départ de la mémoire imprimée dans le programme est d73010, alors que le fichier maps montre que l'adresse de départ de l'espace mémoire du tas est d73000. Pourquoi y a-t-il un 0x10 supplémentaire (16 octets) ? Laissons cette question de côté pour l’instant et parlons-en plus tard. #free La mémoire libérée sera-t-elle restituée au système d'exploitation ?
Exécutons le processus ci-dessus pour voir si la mémoire du tas est toujours là après que la mémoire soit libérée via la fonction free() ?
Comme vous pouvez le voir sur l'image ci-dessous, après avoir libéré la mémoire, la mémoire tas existe toujours et n'a pas été restituée au système d'exploitation.En effet, au lieu de libérer ce 1 octet sur le système d'exploitation, il est préférable de le mettre en cache et de le mettre dans le pool de mémoire de malloc. Lorsque le processus demande à nouveau 1 octet de mémoire, il peut être directement réutilisé, ce qui est possible. est beaucoup plus rapide.
Bien sûr, à la fin du processus, le système d'exploitation récupérera toutes les ressources du processus.
La mémoire du tas existe toujours après que la mémoire libre mentionnée ci-dessus soit destinée à la mémoire appliquée par malloc via la méthode brk().
Si malloc demande de la mémoire via mmap, elle sera renvoyée au système d'exploitation après avoir libéré la mémoire.
Faisons une expérience pour vérifier que nous demandons 128 Ko de mémoire via malloc, afin que malloc alloue de la mémoire via mmap.
#include #include int main() { //申请1字节的内存 void *addr = malloc(128*1024); printf("此128KB字节的内存起始地址:%x\n", addr); printf("使用cat /proc/%d/maps查看内存分配\n",getpid()); //将程序阻塞,当输入任意字符时才往下执行 getchar(); //释放内存 free(addr); printf("释放了128KB字节的内存,内存也归还给了操作系统\n"); getchar(); return 0; }
Code d'exécution :
En regardant la distribution de la mémoire du processus, vous pouvez constater qu'il n'y a pas de marque [head] à l'extrême droite, indiquant que la mémoire anonyme est allouée à partir de la zone de mappage de fichiers via mmap via un mappage anonyme.
Alors libérons cette mémoire et voyons :
Vérifiez à nouveau l'adresse de départ de la mémoire de 128 Ko et vous constaterez qu'elle n'existe plus, indiquant qu'elle a été renvoyée au système d'exploitation.
Concernant la question "La mémoire demandée par malloc et libérée gratuitement sera-t-elle restituée au système d'exploitation ?", on peut faire un résumé :
Ainsi, l'opération de demande de mémoire doit éviter les appels système fréquents. Si mmap est utilisé pour allouer de la mémoire, cela signifie que les appels système doivent être exécutés à chaque fois.
De plus, comme la mémoire allouée par mmap sera restituée au système d'exploitation à chaque fois qu'elle est libérée, l'adresse virtuelle allouée par mmap est donc dans un état de défaut de page à chaque fois, puis lors du premier accès à l'adresse virtuelle. temps, il sera déclenché une interruption de défaut de page.
En d'autres termes,
si la mémoire est fréquemment allouée via mmap, non seulement l'état d'exécution sera changé à chaque fois, mais une interruption de défaut de page se produira également (après le premier accès à l'adresse virtuelle), ce qui entraînera une utilisation importante du processeur. consommation. Afin d'améliorer ces deux problèmes, lorsque malloc demande de la mémoire dans l'espace du tas via l'appel système brk(), puisque l'espace du tas est continu, il pré-alloue directement une mémoire plus grande en tant que pool de mémoire lorsque la mémoire est libérée. , il est mis en cache dans le pool de mémoire.
Lorsque vous demanderez de la mémoire la prochaine fois, retirez simplement le bloc de mémoire correspondant directement du pool de mémoire, et la relation de mappage entre l'adresse virtuelle et l'adresse physique de ce bloc de mémoire peut toujours exister. Cela réduit non seulement le nombre de. appels système, cela réduit également le nombre d'interruptions de faute de page, ce qui réduira considérablement la consommation du processeur.
Puisque brk est tellement génial, pourquoi ne pas utiliser brk pour toutes les allocations ?Si nous demandons successivement trois morceaux de mémoire 10k, 20k et 30k, si 10k et 20k sont libérés et deviennent de l'espace mémoire libre, si la mémoire appliquée pour la prochaine fois est inférieure à 30k, alors cet espace mémoire libre peut être réutilisé .
Mais si la mémoire demandée la prochaine fois est supérieure à 30 Ko, il n'y a pas d'espace mémoire libre disponible et vous devez vous adresser au système d'exploitation, et la mémoire réelle utilisée continuera d'augmenter.Par conséquent, à mesure que le système effectue des mallocs et libère fréquemment, en particulier pour les petits blocs de mémoire, de plus en plus de fragments inutilisables seront générés dans le tas, entraînant des « fuites de mémoire ». Ce phénomène de « fuite » ne peut pas être détecté avec valgrind.
Ainsi, dans l'implémentation de malloc, les différences, avantages et inconvénients dans le comportement de brk et mmap sont pleinement pris en compte, et un gros bloc de mémoire (128 Ko) est alloué par défaut avant que mmap ne soit utilisé pour allouer de l'espace mémoire.
Rappelez-vous, j'ai mentionné plus tôt que l'adresse de départ de la mémoire renvoyée en mode utilisateur par malloc est 16 octets de plus que l'adresse de départ de l'espace de tas du processus ?
Les 16 octets supplémentaires stockent les informations de description du bloc mémoire, telles que la taille du bloc mémoire.
De cette façon, lorsque la fonction free() est exécutée, free décalera l'adresse mémoire entrante de 16 octets vers la gauche, puis analysera la taille du bloc mémoire actuel à partir de ces 16 octets, et saura naturellement quelle est sa taille doit être libéré de la mémoire.
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!