250 votes

Que fait l'appel système brk() ?

Selon le manuel du programmeur Linux :

brk() et sbrk() modifient l'emplacement de la pause du programme, qui définit la fin du segment de données du processus. qui définit la fin du segment de données du processus.

Que signifie le segment de données ici ? C'est juste le segment de données ou les données, BSS et le tas combinés ?

Selon wiki :

Parfois, les zones de données, de BSS et de tas sont collectivement appelées "segment de données".

Je ne vois aucune raison de modifier la taille du seul segment de données. Si ce sont des données, BSS et le tas collectivement alors cela a du sens car le tas aura plus d'espace.

Ce qui m'amène à ma deuxième question. Dans tous les articles que j'ai lus jusqu'à présent, l'auteur dit que le tas croît vers le haut et la pile vers le bas. Mais ce qu'ils n'expliquent pas, c'est ce qui se passe lorsque le tas occupe tout l'espace entre le tas et la pile ?

enter image description here

1 votes

Que faites-vous quand vous n'avez plus d'espace ? Vous changez pour un disque dur. Lorsque vous avez utilisé l'espace, vous le libérez pour d'autres types d'informations.

36 votes

@Igoris : Vous confondez mémoire physique (que vous pouvez échanger sur le disque si nécessaire, en utilisant la mémoire virtuelle) et espace adresse . Lorsque vous remplissez votre espace d'adressage, aucun échange ne vous permettra de récupérer les adresses du milieu.

10 votes

Pour rappel, le brk() L'appel système est plus utile en langage assembleur qu'en C. En C, malloc() doit être utilisé à la place de brk() à des fins d'allocation de données - mais cela n'invalide en rien la question proposée.

302voto

Zack Points 44583

Dans le diagramme que vous avez posté, la "rupture" - l'adresse manipulée par brk y sbrk -est la ligne pointillée en haut du tas.

simplified image of virtual memory layout

La documentation que vous avez lue décrit cela comme la fin du "segment de données" parce que dans les systèmes traditionnels (pré-bibliothèques partagées, pré-bibliothèques en ligne, etc. mmap ) Unix, le segment de données était continu avec le tas ; avant le démarrage du programme, le noyau chargeait les blocs "texte" et "données" dans la RAM en commençant à l'adresse zéro (en fait, un peu au-dessus de l'adresse zéro, de sorte que le pointeur NULL ne pointe véritablement sur rien) et plaçait l'adresse de rupture à la fin du segment de données. Le premier appel à malloc utiliserait alors sbrk pour déplacer la rupture et créer le tas entre les deux le haut du segment de données et la nouvelle adresse de rupture, plus élevée, comme indiqué dans le schéma, et l'utilisation ultérieure de la fonction malloc l'utiliserait pour agrandir le tas si nécessaire.

Pendant ce temps, la pile commence au sommet de la mémoire et se développe vers le bas. La pile n'a pas besoin d'appels système explicites pour s'agrandir ; soit elle commence avec autant de RAM allouée qu'elle ne pourra jamais en avoir (c'est l'approche traditionnelle), soit il existe une région d'adresses réservées sous la pile, à laquelle le noyau alloue automatiquement de la RAM lorsqu'il remarque une tentative d'écriture à cet endroit (c'est l'approche moderne). Dans tous les cas, il peut y avoir ou non une région "de garde" au bas de l'espace d'adressage qui peut être utilisée pour la pile. Si cette région existe (c'est le cas de tous les systèmes modernes), elle est désaffectée de façon permanente. soit la pile ou le tas tente de s'agrandir, vous obtenez une erreur de segmentation. Traditionnellement, cependant, le noyau n'essayait pas de faire respecter une frontière ; la pile pouvait grandir dans le tas, ou le tas pouvait grandir dans la pile, et dans tous les cas, ils gribouillaient sur les données de l'autre et le programme plantait. Si vous étiez très chanceux, il se plantait immédiatement.

Je ne sais pas d'où vient le chiffre 512GB dans ce diagramme. Il implique un espace d'adressage virtuel de 64 bits, ce qui est incompatible avec la carte mémoire très simple que vous avez là. Un véritable espace d'adressage de 64 bits ressemble davantage à ceci :

less simplified address space

              Legend:  t: text, d: data, b: BSS

Ce n'est pas du tout à l'échelle, et il ne faut pas l'interpréter comme la manière exacte dont un système d'exploitation donné fait les choses (après l'avoir dessiné, j'ai découvert que Linux place en fait l'exécutable beaucoup plus près de l'adresse zéro que je ne le pensais, et les bibliothèques partagées à des adresses étonnamment élevées). Les régions noires de ce diagramme ne sont pas mappées -- tout accès provoque un défaut de segmentation immédiat -- et sont les suivantes gigantesque par rapport aux zones grises. Les régions gris clair sont le programme et ses bibliothèques partagées (il peut y avoir des douzaines de bibliothèques partagées) ; chacune d'entre elles a un nom de fichier indépendant le segment de texte et de données (et le segment "bss", qui contient également des données globales mais qui est initialisé à tous les bits zéro plutôt que de prendre de la place dans l'exécutable ou la bibliothèque sur le disque). Le tas n'est plus nécessairement continu avec le segment de données de l'exécutable -- je l'ai dessiné de cette façon, mais il semble que Linux, au moins, ne le fasse pas. La pile n'est plus fixée au sommet de l'espace d'adressage virtuel, et la distance entre le tas et la pile est si énorme que vous n'avez pas à vous soucier de la traverser.

La rupture est toujours la limite supérieure du tas. Cependant, ce que je n'ai pas montré, c'est qu'il pourrait y avoir des douzaines d'allocations indépendantes de mémoire quelque part dans le noir, effectuées avec mmap au lieu de brk . (L'OS s'efforcera de les garder loin de la brk pour qu'ils n'entrent pas en collision).

11 votes

+1 pour une explication détaillée. Savez-vous si malloc s'appuie toujours sur brk ou s'il utilise mmap pour pouvoir "rendre" des blocs de mémoire distincts ?

23 votes

Cela dépend de l'implémentation spécifique, mais IIUC beaucoup d'actuels malloc utilisent le brk zone pour de petites affectations et des mmap s pour les allocations importantes (disons, >128K). Voir, par exemple, la discussion de MMAP_THRESHOLD dans le manuel Linux malloc(3) page de manuel.

3 votes

En effet, une bonne explication. Mais comme vous l'avez dit, la pile ne se trouve plus au sommet de l'espace d'adressage virtuel. Est-ce vrai uniquement pour l'espace d'adressage de 64 bits ou même pour l'espace d'adressage de 32 bits ? Et si la pile se trouve au sommet de l'espace d'adressage, où se trouvent les cartes mémoire anonymes ? Est-ce au sommet de l'espace d'adressage virtuel, juste avant la pile ?

10voto

luser droog Points 9030

Vous pouvez utiliser brk y sbrk vous-même pour éviter le "malloc overhead" dont tout le monde se plaint. Mais vous ne pouvez pas facilement utiliser cette méthode en conjonction avec malloc donc c'est seulement approprié quand vous n'avez pas à free n'importe quoi. Parce que vous ne pouvez pas. De plus, vous devez éviter tout appel à la bibliothèque qui pourrait utiliser malloc en interne. Ie. strlen est probablement sûr, mais fopen ne l'est probablement pas.

Appelez sbrk tout comme vous appelleriez malloc . Elle renvoie un pointeur sur la pause actuelle et incrémente la pause de ce montant.

void *myallocate(int n){
    return sbrk(n);
}

Bien que vous ne puissiez pas libérer des allocations individuelles (parce qu'il n'y a pas de malloc-overhead , souvenez-vous), vous puede gratuit l'ensemble de l'espace en appelant brk avec la valeur renvoyée par le premier appel à sbrk donc rembobiner le brk .

void *memorypool;
void initmemorypool(void){
    memorypool = sbrk(0);
}
void resetmemorypool(void){
    brk(memorypool);
}

Vous pouvez même empiler ces régions, en éliminant la région la plus récente en remontant la pause jusqu'au début de la région.


Une dernière chose...

sbrk est également utile dans code golf car il est plus court de 2 caractères que malloc .

7 votes

-1 parce que : malloc / free très certainement puede (et le font) en rendant de la mémoire au système d'exploitation. Ils ne le font peut-être pas toujours quand vous le voulez, mais c'est une question d'heuristique imparfaitement réglée pour votre cas d'utilisation. Plus important encore, il est dangereux d'appeler sbrk avec un argument non nul dans tout programme qui pourrait appeler malloc -- et presque toutes les fonctions de la bibliothèque C sont autorisées à appeler malloc en interne. Les seuls qui ne le seront certainement pas sont les suivants async-signal-safe fonctions.

0 votes

Et par "ce n'est pas sûr", je veux dire "votre programme va planter".

0 votes

Je l'ai modifié pour enlever le le retour de la mémoire et a mentionné le danger que représentent les fonctions de la bibliothèque utilisant en interne malloc .

5voto

R.. Points 93718

Il existe un mappage spécial de la mémoire privée anonyme (traditionnellement situé juste après les données/bss, mais les Linux modernes ajustent l'emplacement avec ASLR). En principe, ce n'est pas mieux que n'importe quel autre mappage que vous pourriez créer avec la commande mmap mais Linux dispose de certaines optimisations qui permettent d'étendre la fin de ce mappage (en utilisant l'attribut brk syscall) vers le haut, avec un coût de verrouillage réduit par rapport à ce qu'il est possible de faire avec les mmap o mremap serait encourue. Il est donc intéressant pour malloc à utiliser lors de l'implémentation du tas principal.

0 votes

Vous vouliez dire possible pour étendre la fin de cette cartographie vers le haut, oui ?

0 votes

Oui, c'est réparé. Désolé pour cela !

1voto

Anders Abel Points 36203

Le tas est placé en dernier dans le segment de données du programme. brk() est utilisé pour modifier (étendre) la taille du tas. Lorsque le tas ne peut plus croître malloc l'appel échouera.

0 votes

Vous dites donc que tous les diagrammes sur Internet, comme celui de ma question, sont faux. Si possible, pouvez-vous m'indiquer un schéma correct ?

2 votes

@Nikkhil Gardez à l'esprit que le haut de ce diagramme est le fin de la mémoire. Le haut de la pile se déplace vers le bas sur le diagramme au fur et à mesure que la pile grandit. Le sommet de la pile se déplace vers le haut sur le diagramme au fur et à mesure qu'il est développé.

0voto

Brian Gordon Points 2551

Je peux répondre à votre deuxième question. Malloc va échouer et retourner un pointeur nul. C'est pourquoi il faut toujours vérifier la présence d'un pointeur nul lors de l'allocation dynamique de mémoire.

0 votes

Alors à quoi servent brk et sbrk ?

3 votes

@NikhilRathod : malloc() utilisera brk() et/ou sbrk() sous le capot - et vous le pouvez aussi, si vous voulez mettre en œuvre votre propre version personnalisée de l'outil de gestion de la sécurité. malloc() .

0 votes

@Daniel Pryden : comment brk et sbrk peuvent-ils fonctionner sur le heap alors qu'il est entre la pile et le segment de données comme indiqué dans le diagramme ci-dessus. pour que cela fonctionne le heap devrait être à la fin. N'ai-je pas raison ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X