Quelles sont les limites du tas?

Quelles sont les limites du tas dans un processus donné? Je comprends qu’il n’y a probablement pas de réponse simple à cette question, alors je suis intéressé par les réponses aux questions suivantes:

  • Existe-t-il une taille de stack / emplacement standard pour les processus 64 bits sous Linux sur AMD64?
  • Si j’implémente une langue d’exécution, comment puis-je savoir où je ne suis pas autorisé à mettre le tas (encore une fois, Linux / AMD64)
  • Existe-t-il un moyen portable pour une application de savoir où elle commence / se termine?

Je suppose que vous essayez d’écrire votre propre allocateur de tas ici, et à partir des balises, supposez que vous le faites sous Linux.

SunEric vous a donné une indication utile de la mémoire que vous pouvez utiliser, cependant, la mémoire que vous pouvez utiliser est la mémoire que le système d’exploitation vous fournit. IE pour obtenir de la mémoire dans votre processus, vous devrez appeler le système d’exploitation pour mapper la mémoire virtuelle dans l’espace de processus (et une partie de la mémoire physique derrière). malloc() extrait ceci pour vous et implémente ‘le tas’ en C. Il peut récupérer sa mémoire de deux manières:

  1. Utilisation de l’appel système brk (mappé à la bibliothèque C brk ou sbrk )

  2. Utiliser mmap avec MAP_ANON (ou plus précisément l’appel système sous-jacent mmap2 ).

brk est la méthode classique d’allocation de mémoire pour le tas, et normalement quand on parle du tas, on entend la mémoire allouée de cette façon (bien que brk puisse être utilisé pour allouer de la mémoire autre que le tas, et les éléments de tas peuvent vivre ailleurs) – voir ci-dessous). Voici une excellente réponse à la question de savoir comment fonctionne l’allocation brk , sur laquelle je ne peux pas m’améliorer. L’emplacement utilisé par la mémoire est en réalité un résultat de l’arithmétique. Le tas suit le BSS du programme lorsqu’il est chargé – c’est-à-dire que la valeur du BSS augmente à mesure que le tas se développe, de sorte que le démarrage est vraiment déterminé par le système d’exploitation et le chargeur dynamic. La fin du tas est donc déterminée par celle-ci et par la taille du tas (c.-à-d. Sa taille).

mmap est moins net. Il faut un paramètre addr :

Si addr est NULL , le kernel choisit l’adresse à laquelle créer le mappage; C’est la méthode la plus portable pour créer un nouveau mappage. Si addr n’est pas NULL , le kernel le prend comme un indice sur l’emplacement du mappage; sous Linux, le mappage sera créé à une limite de page proche. L’adresse du nouveau mappage est renvoyée à la suite de l’appel.

Donc, si vous utilisez mmap pour obtenir de l’espace pour des éléments de malloc particuliers (comme malloc peut le faire en particulier pour les objects de grande taille), le système d’exploitation choisit son emplacement, avec ou sans indice. Si vous utilisez MAP_FIXED il vous donnera exactement cet emplacement ou échouera. En ce sens, votre segment de mémoire (ou les éléments qu’il contient) peut se trouver partout où le système d’exploitation vous permet de mapper la mémoire.

Vous avez demandé s’il existe un moyen portable de savoir où le tas commence et se termine. Portable implique un langage, et je supposerai C. En ce qui concerne le tas de type brk , oui il y a (bien raisonnablement portable). man end donne:

PRÉNOM

etext , edata , fin de segment de programme

SYNOPSIS

extern etext;

extern edata;

extern end;

LA DESCRIPTION

Les adresses de ces symboles indiquent la fin de divers segments de programme:

  • etext : C’est la première adresse après la fin du segment de texte (le code du programme).

  • edata : Il s’agit de la première adresse après la fin du segment de données initialisé.

  • end : c’est la première adresse après la fin du segment de données non initialisé (également appelé segment BSS).

Comme le tas s’exécute de la fin du BSS au moment du chargement au sumt du BSS au moment de l’exécution, une approche consisterait à prendre la valeur end at load comme début du bas du tas et la valeur de end lorsque en évaluant comme la fin du tas. Cela manquerait le fait que libc elle-même et les bibliothèques partagées peuvent allouer des choses avant que main() soit appelée. Donc, une approche plus conservasortingce serait de dire que c’est la zone entre edata et end , bien que cela puisse à proprement parler inclure des choses qui ne sont pas sur le tas.

Si vous ne vouliez pas dire en C, vous devez utiliser une technique similaire. Prenez la “pause programme” (c.-à-d. La partie supérieure de l’espace mémoire) et soustrayez l’adresse la plus basse que vous avez donnée pour votre segment de mémoire.

Si vous voulez voir l’allocation de mémoire pour le tas pour un processus arbitraire:

 $ cat /proc/$$/maps | fgrep heap 01fe6000-02894000 rw-p 00000000 00:00 0 [heap] 

Remplacez $$ par le PID du processus à examiner.

Sur les processeurs AMD64 64 bits modernes, toutes les lignes d’adresse ne sont pas autorisées à nous fournir 2^64 = 16 exabytes d’espace d’adressage virtuel. Peut-être que sur les architectures AMD64, 48 bits inférieurs sont activés respectivement, ce qui donne 2^48 = 256TB d’espace d’adressage. Ainsi théoriquement, l’architecture se limite à près de 256TB . Donc, si vous avez un espace disque de 256TB qui est autorisé pour le partitionnement de swap, vous pouvez obtenir 256TB de tas. Si le nombre et la taille des partitions d’échange sont limités, vous êtes limité à moins de 256TB même si l’espace disque disponible est important.

Dans l’implémentation 48 bits actuelle d’AMD, la plage de mémoire virtuelle complète que les processeurs AMD64 peuvent adresser au format canonique (illustré ci-dessous) est 00007FFFFFFFFFFF en deux parties allant de 0 à 00007FFFFFFFFFFF et de FFFF800000000000 à FFFF800000000000 à 256TB . La moitié supérieure de l’espace d’adressage de la région mémoire est destinée à l’espace kernel et la moitié inférieure correspond à l’espace utilisateur pour les segments de code, de segment de mémoire et de stack. Ainsi, les bits d’adresse de la moitié inférieure croissent vers le haut avec la disponibilité d’un plus grand nombre de bits d’adresse virtuelle, ce qui conduit à davantage d’espace virtuel pour mapper différents segments dans la mémoire. Ce qui signifie que le tas peut atteindre 256TB maximum.

  0xFFFFFFFFFFFFFFFF +-----------+ | Kernel | | | 0xFFFF800000000000 +-----------+ | Non | | Canonical | | range | 0x00007FFFFFFFFFFF +-----------+ | User | | | 0x0 +-----------+ 

Cependant, le tas commence au-dessus du segment de texte qui grandit et une extrémité peut être trouvée en utilisant sbrk avec l’argument 0. Comme le tas n’est pas continu lorsque vous appelez malloc (), il renvoie l’adresse de n’importe où dans l’espace d’adressage virtuel.

Vous ne devriez pas vous inquiéter de la manière dont il fonctionne à partir des racines car il est extrait dans les processeurs modernes.