web-dev-qa-db-fra.com

Comment malloc () est-il implémenté en interne?

Quelqu'un peut-il expliquer comment malloc() fonctionne en interne?

J'ai parfois fait strace program Et je vois beaucoup d'appels système sbrk, en faisant man sbrk Parler de son utilisation dans malloc() mais pas beaucoup plus.

108
bodacydo

L'appel sbrksystem déplace la "bordure" du segment de données. Cela signifie qu’il déplace la bordure d’une zone dans laquelle un programme peut lire/écrire des données (en les laissant croître ou rétrécir, bien qu’apparemment, AFAIK no malloc restitue réellement des segments de mémoire au noyau avec cette méthode). En plus de cela, il y a aussi mmap qui est utilisé pour mapper des fichiers en mémoire mais également pour allouer de la mémoire (si vous avez besoin d'allouer de la mémoire partagée, mmap est comme vous le faites).

Vous avez donc deux méthodes pour obtenir plus de mémoire du noyau: sbrk et mmap. Il existe différentes stratégies pour organiser la mémoire obtenue à partir du noyau.

Une méthode naïve consiste à la partitionner en zones, souvent appelées "seaux", dédiées à certaines tailles de structure. Par exemple, une implémentation malloc pourrait créer des compartiments pour des structures de 16, 64, 256 et 1024 octets. Si vous demandez à malloc de vous donner une mémoire d'une taille donnée, il arrondit ce nombre à la taille du compartiment suivant, puis vous fournit un élément de ce compartiment. Si vous avez besoin d'une plus grande surface, malloc peut utiliser mmap pour allouer directement avec le noyau. Si le compartiment d'une certaine taille est vide, malloc peut utiliser sbrk pour obtenir plus d'espace pour un nouveau compartiment.

Il existe divers malloc conceptions et il n’existe probablement pas de véritable moyen d’implémenter malloc, car vous devez faire un compromis entre vitesse, temps système et éviter la fragmentation/efficacité de l’espace. Par exemple, si un compartiment est à court d'éléments, une implémentation peut extraire un élément d'un compartiment plus grand, le scinder et l'ajouter au compartiment qui n'a plus d'éléments. Ce serait très efficace en termes d'espace, mais ne serait pas possible avec tous les modèles. Si vous venez d’obtenir un autre compartiment via sbrk/mmap, cela pourrait être plus rapide et encore plus simple, mais sans gagner autant en espace. En outre, la conception doit bien sûr prendre en compte le fait que "libre" doit libérer de l’espace pour malloc à nouveau. Vous ne distribuez pas simplement de la mémoire sans la réutiliser.

Si cela vous intéresse, le proxy OpenSER/Kamailio SIP dispose de deux implémentations malloc (elles ont besoin de la leur, car elles utilisent beaucoup la mémoire partagée et le système malloc ne prend pas en charge la mémoire partagée. Voir: https://github.com/OpenSIPS/opensips/tree/master/mem

Ensuite, vous pouvez aussi regarder GNU libc malloc implementation , mais celui-ci est très compliqué, IIRC.

100
DarkDust

Malloc simpliste et travail libre comme celui-ci:

malloc donne accès au tas d'un processus. Le tas est une construction de la bibliothèque principale C (généralement libc) qui permet aux objets d'obtenir un accès exclusif à un espace sur le tas du processus.

Chaque allocation sur le tas s'appelle une cellule de tas. Cela consiste généralement en un en-tête contenant des informations sur la taille de la cellule, ainsi qu'un pointeur sur la cellule de segment suivante. Cela fait un tas effectivement une liste chaînée.

Lors du démarrage d'un processus, le segment de mémoire contient une seule cellule contenant tout l'espace de segment de mémoire attribué au démarrage. Cette cellule existe sur la liste libre du tas.

Quand on appelle malloc, la mémoire est extraite de la grande cellule de tas, qui est renvoyée par malloc. Le reste est formé dans une nouvelle cellule de tas qui comprend tout le reste de la mémoire.

Quand on libère de la mémoire, la cellule de tas est ajoutée à la fin de la liste des tas libres. Les mallocs suivants parcourent la liste libre à la recherche d'une cellule de taille appropriée.

Comme on peut s'y attendre, le segment de mémoire peut être fragmenté et le gestionnaire de segment de mémoire peut, de temps à autre, essayer de fusionner des cellules de segment adjacentes.

Lorsqu'il ne reste plus de mémoire sur la liste disponible pour l'allocation souhaitée, malloc appelle brk ou sbrk, qui sont les appels système demandant plus de pages de mémoire au système d'exploitation.

Il existe maintenant quelques modifications pour optimiser les opérations de tas.

  • Pour les allocations de mémoire importantes (généralement> 512 octets, le gestionnaire de tas peut aller directement au système d'exploitation et allouer une page de mémoire complète.
  • Le segment de mémoire peut spécifier une taille minimale d'allocation afin d'éviter de grandes quantités de fragmentation.
  • Le segment de mémoire peut également se diviser en deux catégories: une pour les allocations faibles et une pour les allocations plus importantes afin d'accélérer les allocations plus importantes.
  • Il existe également des mécanismes astucieux pour optimiser l'allocation de tas multithreads.
45
doron

Il est également important de réaliser que déplacer simplement le pointeur de rupture de programme avec brk et sbrk ne fait pas allouer la mémoire, il configure simplement l'espace d'adressage. Sous Linux, par exemple, la mémoire sera "sauvegardée" par les pages physiques réelles lors de l'accès à cette plage d'adresses, ce qui entraînera une erreur de page et conduira éventuellement au noyau à appeler la page d'allocation pour obtenir une page de sauvegarde.

7
mgalgs