Dupliquer possible:
Comment free et malloc fonctionnent-ils en C?
Prenons un scénario dans lequel je dois allouer environ 20 octets de mémoire via malloc. Pour que l’appel de fonction à malloc () réussisse, les 20 octets doivent-ils être disponibles de manière contiguë en mémoire ou peuvent-ils être dispersés? Par exemple, dans le cas ci-dessus, s’il y a 4 ou 5 morceaux de 10 octets chacun, malloc fonctionnera-t-il? Ou est-ce spécifique à l’OS ou au compilateur?
La question est un peu fausse.
Dans un système d’exploitation typique, il existe les concepts de mémoire virtuelle et de mémoire physique.
La mémoire physique existe généralement en blocs de 4 Ko, la mémoire virtuelle également.
Chaque processus a une mémoire virtuelle – à chaque processus, le système d’exploitation présente ce qui semble être la plage de mémoire entièrement adressable. Ainsi, sur une machine 32 bits, chaque processus “pense” qu’il dispose de 4 Go de mémoire contigu.
En réalité, le système d’exploitation, en coulisse, est en train de mapper des allocations de mémoire virtuelle sur de vrais blocs de mémoire physique. Ainsi, une allocation de mémoire virtuelle de 400 Ko, par exemple, est mappée sur 100 blocs physiques. Ces blocs physiques n’ont pas besoin d’être contigus (et presque jamais – rien ne l’empêche de se produire, mais sur une machine effectuant tout type de travail, c’est hautement improbable), mais l’allocation de mémoire virtuelle doit être contigu.
Donc, vous pouvez toujours courir dans la fragmentation de la mémoire virtuelle. Ici, un processus demande un bloc de mémoire et il n’y a pas, dans ces processus particuliers, de mappage de mémoire virtuelle, un bloc de mémoire virtuelle contiguë permettant de satisfaire la demande.
Ce problème est le problème auquel vous pensez.
L’appel à malloc
réussira soit à renvoyer un bloc de mémoire logiquement contigu à partir de l’espace mémoire HEAP de votre programme égal à la taille demandée, soit il échouera avec un pointeur NULL. “Logiquement contigu” signifie qu’avec un malloc
de ce type:
int *ip; /* Nothing yet allocated (other than the size of a pointer... */ int ar[100]; /* 100 ints on the STACK */ ip = (int *)malloc(sizeof ar); /* if that succeeds, 100 ints on the HEAP */
va allouer de l’espace pour 100 ints sur votre système d’exploitation sur le HEAP et renvoyer NULL ou le pointeur. Séparément, le tableau ar
est alloué sur la stack. Chaque tableau sera mis en place avec toutes les entrées logiquement côte à côte, au moins autant que votre programme le sait. S’ils ne sont pas côte à côte, vous ne pourrez pas adresser ces blocs sous forme de tableaux avec la notation array[offset]
ou avec l’arithmétique de pointeur.
Vous pouvez alors accéder aux blocs de mémoire STACK ou HEAP avec un access tableau ou un pointeur de manière interchangeable comme ceci:
ip[2]=22; /* the second element of ip[] is '22' */ *(ar+33)=3333; /* the 33 element of ar is '3333' */ i=*(ip+2); /* assign using pointers */ j=ar[33]; /* assign using array offsets */
Si le bloc de mémoire renvoyé par malloc
n’était pas logiquement contigu à votre programme, vous ne pourrez pas accéder au bloc avec une arithmétique de pointeur ou un indice en tableau.
En arrière-plan, votre système d’exploitation peut déplacer d’autres blocs de mémoire déplaçables, utiliser la mémoire virtuelle, échanger d’autres éléments en mémoire virtuelle, etc., afin d’augmenter le nombre de points HEAP alloués à votre programme. Malloc peut être un appel très rapide ou très coûteux – en fonction de ce qui se passe sur ce système et de l’espace HEAP alloué à votre programme.
La mémoire HEAP (la partie accessible avec les appels système dynamics en C) est potentiellement sujette à la fragmentation. Supposons que vous allouiez le nombre de blocs de 20 octets pour lesquels la mémoire devient rare. Maintenant, imaginez que vous libérez tous les autres blocs de ces blocs. Vous aurez une mémoire très fragmentée car les blocs alloués avec malloc
ne peuvent pas être déplacés si cela affecte le pointeur que le programme utilise pour accéder au bloc. (Il peut être déplacé de manière transparente, mais ne comptez pas sur son efficacité.)
Si vous passez de nombreux appels à la mémoire HEAP, envisagez de modifier votre logique pour utiliser realloc
afin de l’agrandir et de la réduire au besoin. Un grand ‘gotcha’ avec realloc est que le pointeur sur vos données existantes est susceptible de changer, utilisez donc seulement 1 pointeur dessus. Realloc permet au système d’exploitation de déplacer les données selon les besoins pour mieux s’adapter à ce qui est disponible sur le HEAP. Vous éviterez (principalement) le risque de fragmentation de la mémoire de cette façon.
Pour des blocs rapides de 20 octets, envisagez d’utiliser le STACK. C’est ce que c’est pour. Regardez cet article SO pour voir les caractéristiques de STACK vs HEAP.
Lisez le Guide C de calloc, malloc, realloc, gratuit pour plus d’informations.
malloc
standard est défini dans le standard C pour allouer un bloc de mémoire contigu (du moins, il vous semble ainsi) – il renverra un pointeur nul si l’atsortingbution échoue.
À un niveau inférieur, le système d’exploitation fera quelque chose comme ce que kotlinski ou Blank Xavier ont décrit dans leurs réponses respectives.
A partir du §7.20.3 de la norme ISO / IEC 9899-1999 C :
Le pointeur renvoyé si l’allocation (par
calloc
,realloc
oumalloc
) réussit est correctement aligné de manière à pouvoir être affecté à un pointeur sur n’importe quel type d’object, puis utilisé pour accéder à un tel object ou à un tableau de tels objects dans l’espace. alloué (jusqu’à ce que l’espace soit explicitement libéré).
Ce n’est pas explicite, mais le paragraphe mentionne “accéder à un tableau de tels objects”, et dans le standard C, les tableaux sont les suivants:
Un type de tableau décrit un ensemble d’objects non vides alloués de manière contiguë avec un type d’object membre particulier, appelé type d’élément. (à partir de §6.2.5 )
Notez également que les appels ultérieurs à calloc
, realloc
et malloc
ne garantissent pas la contiguïté ni la commande de mémoire (avec d’autres blocs de mémoire déjà alloués).
Ce point est également spécifié au §7.20.3 .
L’ordre et la contiguïté du stockage alloué par les appels successifs aux fonctions
calloc
,malloc
etrealloc
sont indéterminés.
Ceci est spécifique à la plate-forme. A vos yeux, au niveau du logiciel, il sera toujours présenté comme vous le feriez pour: 20 octets contigus. Mais sur certaines plates-formes, par exemple avec de la mémoire paginée, ces octets n’ont pas vraiment besoin d’être contigus s’agissant du matériel réel;
La mémoire doit être continue – ce que vous mentionnez est généralement appelé fragmentation de la mémoire et constitue un réel problème pour les applications qui acquièrent et libèrent de la mémoire très souvent.
OS spécifique.
S’il s’agit d’un système doté de mémoire virtuelle, malloc va simplement allouer une autre page de machine virtuelle et l’append à son segment de mémoire, puis vous remettre la mémoire dont vous avez besoin. La partie de la mémoire dans la page doit être contiguë cependant. Fait intéressant, si votre demande est supérieure à la taille de la page, la mémoire ne doit pas nécessairement être contiguë dans la mémoire physique. Deux pages de machine virtuelle peuvent être rendues contiguës, quel que soit leur emplacement dans la mémoire physique.
Si c’est un système qui n’a pas de machine virtuelle, alors oui, toute la mémoire demandée doit être contiguë dans la mémoire physique.
De la norme ISO C99 :
7.20.3 Fonctions de gestion de la mémoire
L’ordre et la contiguïté du stockage alloué par les appels successifs aux fonctions calloc, malloc et realloc sont indéterminés. Le pointeur renvoyé si l’allocation réussit est correctement aligné de sorte qu’il puisse être affecté à un pointeur sur tout type d’object, puis utilisé pour accéder à un tel object ou à un tableau d’objects de ce type dans l’espace alloué (jusqu’à ce que l’espace soit explicitement désalloué). . La durée de vie d’un object alloué s’étend de l’allocation jusqu’à la désallocation. Chacune de ces atsortingbutions doit donner un pointeur sur un object disjoint de tout autre object. Le pointeur a renvoyé des points au début (adresse d’octet le plus bas) de l’espace alloué. Si l’espace ne peut pas être alloué, un pointeur null est renvoyé. Si la taille de l’espace demandé est égale à zéro, le comportement est défini par l’implémentation: soit un pointeur null est renvoyé, soit le comportement est comme si la taille était une valeur différente de zéro, sauf que le pointeur renvoyé ne doit pas être utilisé pour accéder à un object. .
7.20.3.3 La fonction malloc
La fonction malloc alloue de l’espace pour un object dont la taille est spécifiée par la size
et dont la valeur est indéterminée.
En d’autres termes, si vous demandez de la place pour un object de 20 octets, vous disposez de suffisamment d’espace pour y insérer un object de 20 octets.
Cela ne fonctionnera probablement pas. malloc ne peut pas réorganiser la mémoire déjà allouée et ne peut donc pas créer un bloc libre continu de 20 octets. Il ne peut que demander au système d’exploitation de se procurer une autre page de mémoire sur laquelle il peut découper 20 octets + en-tête.