Al compilar código C y mirar el ensamblaje, todo hace que la pila crezca hacia atrás de la siguiente manera:
_main:
pushq %rbp
movl $5, -4(%rbp)
popq %rbp
ret
-4(%rbp)
- ¿Significa esto que el puntero base o el puntero de la pila en realidad están bajando las direcciones de memoria en lugar de subir? ¿Porqué es eso?
Cambié $5, -4(%rbp)
a $5, +4(%rbp)
, compilé y ejecuté el código y no hubo errores. Entonces, ¿por qué todavía tenemos que retroceder en la pila de memoria?
-4(%rbp)
no mueve el puntero base en absoluto y que+4(%rbp)
no podría haber funcionado.malloc
crece el montón hacia atrásmain
cambia su RBP. Eso es ciertamente posible. (Y sí, escribir4(%rbp)
pisaría el valor RBP guardado). Err en realidad, este principal nunca lo hacemov %rsp, %rbp
, por lo que el acceso a la memoria es relativo al RBP de la persona que llama , si eso es lo que el OP realmente probó. Si esto se copió de la salida del compilador, ¡se omitieron algunas instrucciones!Respuestas:
Sí, las
push
instrucciones disminuyen el puntero de la pila y escriben en la pila, mientras quepop
hacen lo contrario, leen la pila e incrementan el puntero de la pila.Esto es algo histórico en el sentido de que para máquinas con memoria limitada, la pila se colocó alta y se hizo crecer hacia abajo, mientras que el montón se colocó bajo y se hizo crecer hacia arriba. Solo hay una brecha de "memoria libre": entre el montón y la pila, y esta brecha se comparte, cualquiera puede crecer en la brecha según sea necesario individualmente. Por lo tanto, el programa solo se queda sin memoria cuando la pila y el montón chocan sin dejar memoria libre.
Si la pila y el montón crecen en la misma dirección, entonces hay dos huecos, y la pila realmente no puede crecer en la brecha del montón (lo contrario también es problemático).
Originalmente, los procesadores no tenían instrucciones de manejo de pila dedicadas. Sin embargo, a medida que se agregó soporte de pila al hardware, adoptó este patrón de crecimiento descendente, y los procesadores aún siguen este patrón hoy.
Se podría argumentar que en una máquina de 64 bits hay suficiente espacio de direcciones para permitir múltiples espacios, y como evidencia, múltiples espacios son necesariamente el caso cuando un proceso tiene múltiples hilos. Aunque esto no es suficiente motivación para cambiar las cosas, ya que con los sistemas de brechas múltiples, la dirección del crecimiento es posiblemente arbitraria, por lo que la tradición / compatibilidad inclina la balanza.
Habría que cambiar las instrucciones de manejo de la CPU pila con el fin de cambiar la dirección de la pila, o bien renunciar a la utilización de las empujando y haciendo estallar las instrucciones (por ejemplo, dedicados
push
,pop
,call
,ret
, otros).Tenga en cuenta que la arquitectura del conjunto de instrucciones MIPS no tiene
push
& dedicadopop
, por lo que es práctico hacer crecer la pila en cualquier dirección: aún puede desear un diseño de memoria de un espacio para un proceso de subproceso único, pero podría aumentar la pila hacia arriba y el montón hacia abajo. Sin embargo, si hiciera eso, algunos códigos de C varargs podrían requerir un ajuste en la fuente o en el paso de parámetros bajo el capó.(De hecho, dado que no hay un manejo dedicado de la pila en MIPS, podríamos usar pre o post incremento o pre o post decremento para empujar a la pila siempre que usemos el reverso exacto para saltar de la pila, y también asumiendo que el sistema operativo respeta el modelo de uso de pila elegido. De hecho, en algunos sistemas embebidos y algunos sistemas educativos, la pila MIPS crece hacia arriba).
fuente
push
ypop
en la mayoría de las arquitecturas, sino también la interrupción de manipulación mucho más importante,call
,ret
, y todo lo que se ha horneado en interacción con la pila.gets()
, o hace algostrcpy()
que no está en línea, entonces el retorno en esas funciones de la biblioteca utilizará la dirección de retorno sobrescrita. Actualmente con pilas de crecimiento descendente, es cuando regresa la persona que llama.strcpy
), en un arco donde la dirección de retorno se mantiene en un registro a menos que deba ser derramada, no hay acceso a clobber el retorno habla a.En su sistema específico, la pila comienza desde una dirección de memoria alta y "crece" hacia abajo a direcciones de memoria baja. (el caso simétrico de menor a mayor también existe)
Y dado que cambiaste de -4 y +4 y se ejecutó, no significa que sea correcto. El diseño de memoria de un programa en ejecución es más complejo y depende de muchos otros factores que pueden contribuir al hecho de que no se bloqueó instantáneamente en este programa extremadamente simple.
fuente
El puntero de la pila apunta al límite entre la memoria de pila asignada y la no asignada. Crecerlo hacia abajo significa que apunta al comienzo de la primera estructura en el espacio de pila asignado, con otros elementos asignados que siguen en direcciones más grandes. Hacer que los punteros apunten al inicio de las estructuras asignadas es mucho más común que al revés.
Ahora en muchos sistemas en estos días, hay un registro separado para los marcos de la pila que se pueden desenrollar de manera confiable para descubrir la cadena de llamadas, con almacenamiento local variable intercalado. La forma en que este registro de marco de pila se configura en algunas arquitecturas significa que termina apuntando detrás del almacenamiento de variables locales en lugar del puntero de pila anterior . Entonces, el uso de este registro de marco de pila requiere una indexación negativa.
Tenga en cuenta que los marcos de pila y su indexación son un aspecto secundario de los lenguajes informáticos compilados, por lo que es el generador de código del compilador el que tiene que lidiar con la "antinaturalidad" en lugar de un mal programador de lenguaje ensamblador.
Entonces, si bien hubo buenas razones históricas para elegir que las pilas crezcan hacia abajo (y algunas de ellas se conservan si programa en lenguaje ensamblador y no se molesta en configurar un marco de pila adecuado), se han vuelto menos visibles.
fuente