web-dev-qa-db-fra.com

Pourquoi faisons-nous encore croître la pile en arrière?

Lorsque vous compilez du code C et que vous regardez Assembly, tout cela fait que la pile se rétracte comme ceci:

_main:
    pushq   %rbp
    movl    $5, -4(%rbp)
     popq    %rbp
    ret

-4(%rbp) - cela signifie-t-il que le pointeur de base ou le pointeur de pile descendent réellement les adresses mémoire au lieu de monter? Pourquoi donc?

J'ai changé $5, -4(%rbp) en $5, +4(%rbp), compilé et exécuté le code et il n'y a pas eu d'erreurs. Alors, pourquoi devons-nous toujours revenir en arrière sur la pile de mémoire?

47
alex

Est-ce à dire que le pointeur de base ou le pointeur de pile descendent réellement les adresses mémoire au lieu de monter? Pourquoi donc?

Oui, les instructions Push décrémentent le pointeur de pile et écrivent dans la pile, tandis que les pop font l'inverse, lisent dans la pile et incrémentent le pointeur de pile.

Ceci est quelque peu historique dans la mesure où pour les machines à mémoire limitée, la pile a été placée en hauteur et agrandie vers le bas, tandis que le tas a été placé en hauteur et agrandi. Il n'y a qu'un seul espace de "mémoire libre" - entre le tas et la pile, et cet espace est partagé, l'un ou l'autre peut se transformer en espace selon les besoins individuels. Ainsi, le programme ne manque de mémoire que lorsque la pile et le tas entrent en collision, ne laissant aucune mémoire libre.

Si la pile et le tas croissent tous les deux dans la même direction, alors il y a deux espaces, et la pile ne peut pas vraiment se développer dans l'espace du tas (l'inverse est également problématique).

À l'origine, les processeurs n'avaient pas d'instructions de gestion de pile dédiées. Cependant, comme le support de pile a été ajouté au matériel, il a pris ce modèle de croissance vers le bas, et les processeurs suivent toujours ce modèle aujourd'hui.

On pourrait faire valoir que sur une machine 64 bits, il y a suffisamment d'espace d'adressage pour permettre plusieurs lacunes - et comme preuve, plusieurs lacunes sont nécessairement le cas lorsqu'un processus a plusieurs threads. Bien que ce ne soit pas une motivation suffisante pour changer les choses, car avec plusieurs systèmes de lacunes, la direction de la croissance est sans doute arbitraire, donc la tradition/compatibilité fait pencher la balance.


Vous devrez modifier les instructions de gestion de la pile CPU afin de changer la direction de la pile, ou bien abandonner l'utilisation des instructions de poussée et d'éclatement dédiées (par exemple Push, pop, call, ret, autres).

Notez que l'architecture du jeu d'instructions MIPS ne dispose pas de Push & pop dédiés, il est donc pratique d'agrandir la pile dans les deux sens - vous pouvez toujours souhaiter une disposition de mémoire à un espace pour un seul processus de thread, mais pourrait augmenter la pile vers le haut et le tas vers le bas. Si vous avez fait cela, cependant, certains codes C varargs pourraient nécessiter un ajustement de la source ou du passage de paramètres sous le capot.

(En fait, comme il n'y a pas de gestion de pile dédiée sur MIPS, nous pourrions utiliser pré ou post incrément ou pré ou post décrément pour pousser sur la pile tant que nous avons utilisé l'inverse exact pour faire sauter la pile, et en supposant également que le Le système d'exploitation respecte le modèle d'utilisation de pile choisi. En effet, dans certains systèmes embarqués et certains systèmes éducatifs, la pile MIPS est agrandie.)

87
Erik Eidt

Dans votre système spécifique, la pile commence à partir d'une adresse mémoire élevée et "croît" vers le bas à des adresses mémoire faibles. (le cas symétrique de bas en haut existe également)

Et puisque vous avez changé de -4 et +4 et qu'il a fonctionné, cela ne signifie pas que c'est correct. La configuration de la mémoire d'un programme en cours d'exécution est plus complexe et dépend de nombreux autres facteurs qui peuvent contribuer au fait que vous ne vous êtes pas immédiatement écrasé sur ce programme extrêmement simple.

8
nadir

Le pointeur de pile pointe à la frontière entre la mémoire de pile allouée et non allouée. L'augmenter vers le bas signifie qu'il pointe vers le début de la première structure dans l'espace de pile alloué, avec d'autres éléments alloués suivant à des adresses plus grandes. Avoir des pointeurs pointant vers le début des structures allouées est beaucoup plus courant que l'inverse.

De nos jours, sur de nombreux systèmes, il existe un registre séparé pour la pile frames qui peut être déroulé de manière assez fiable afin de comprendre la chaîne d'appels, avec un stockage variable local intercalé. La façon dont ce registre de trame de pile est configuré sur certaines architectures signifie qu'il finit par pointer derrière le stockage de variable local par opposition au pointeur de pile avant il. L'utilisation de ce registre de trame de pile nécessite donc une indexation négative.

Notez que les cadres de pile et leur indexation sont un aspect secondaire des langages informatiques compilés, c'est donc le générateur de code du compilateur qui doit faire face à la "contre nature" plutôt qu'un mauvais programmeur de langage d'assemblage.

Ainsi, bien qu'il y ait de bonnes raisons historiques de choisir des piles pour croître vers le bas (et certaines d'entre elles sont conservées si vous programmez en langage assembleur et ne vous embêtez pas à configurer un cadre de pile approprié), elles sont devenues moins visibles.

1
user327022