callstack - teorico - Explicar el concepto de un marco de pila en pocas palabras
stack pointer (6)
"Una pila de llamadas se compone de marcos de pila ..." - Wikipedia
Un marco de pila es una cosa que pones en la pila. Son estructuras de datos que contienen información sobre las subrutinas para llamar.
Parece que tengo la idea de la pila de llamadas en el diseño del lenguaje de programación. Pero no puedo encontrar (probablemente, simplemente no busco lo suficiente) alguna explicación decente sobre qué es el marco de pila .
Entonces me gustaría pedirle a alguien que me explique en pocas palabras.
El marco de pila es la información empaquetada relacionada con una llamada de función. Esta información generalmente incluye argumentos pasados a la función, variables locales y dónde volver al finalizar. Registro de activación es otro nombre para un marco de pila. La disposición del marco de pila se determina en el ABI por el fabricante y cada compilador que soporta el ISA debe cumplir con este estándar, sin embargo, el esquema de disposición puede depender del compilador. En general, el tamaño del marco de pila no está limitado, pero existe un concepto llamado "zona roja / protegida" para permitir que las llamadas al sistema ... etc se ejecuten sin interferir con un marco de pila.
Siempre hay un SP, pero en algunos ABI (ARM y PowerPC, por ejemplo) FP es opcional. Los argumentos que deben colocarse en la pila se pueden compensar utilizando solo el SP. Que una trama de pila se genere para una llamada a función o no depende del tipo y la cantidad de argumentos, las variables locales y cómo se accede a las variables locales en general. En la mayoría de las ISA, en primer lugar, se utilizan registros y si hay más argumentos que registros dedicados a pasar argumentos, estos se colocan en la pila (por ejemplo, x86 ABI tiene 6 registros para pasar argumentos enteros). Por lo tanto, a veces, algunas funciones no necesitan un marco de pila para colocarse en la pila, solo la dirección de retorno se coloca en la pila.
Los programadores pueden tener preguntas sobre los marcos de pila no en un término amplio (que es una entidad única en la pila que sirve solo una llamada de función y mantiene la dirección de retorno, los argumentos y las variables locales) pero en un sentido estricto: cuando el término mencionado en el contexto de las opciones del compilador.
Si el autor de la pregunta lo ha significado o no, pero el concepto de un marco de pila desde el aspecto de las opciones del compilador es un tema muy importante, no cubierto por las otras respuestas aquí.
Por ejemplo, el compilador C / C ++ de Microsoft Visual Studio 2015 tiene la siguiente opción relacionada con stack frames
:
- / Oy (omisión de marco de puntero)
GCC tiene lo siguiente:
- -fomit-frame-pointer (No mantenga el puntero del marco en un registro para las funciones que no lo necesitan. Esto evita las instrucciones para guardar, configurar y restaurar punteros de cuadros, sino que también hace que un registro adicional esté disponible en muchas funciones )
El compilador Intel C ++ tiene lo siguiente:
- -fomit-frame-puntero (Determina si EBP se usa como un registro de propósito general en optimizaciones)
que tiene el siguiente alias:
- / Oy
Delphi tiene la siguiente opción de línea de comandos:
- - $ W + (Generar marcos de pila)
En ese sentido específico, desde la perspectiva del compilador, un marco de pila es solo el código de entrada y salida de la rutina , que empuja un ancla a la pila, que también puede usarse para la depuración y el manejo de excepciones. Las herramientas de depuración pueden escanear los datos de la pila y usar estos anclajes para el trazado inverso, mientras ubican los call sites
en la pila, es decir, para mostrar los nombres de las funciones en el orden en que se les ha llamado jerárquicamente. Para la arquitectura Intel, es push ebp; mov ebp, esp
push ebp; mov ebp, esp
o enter
para entrada y mov esp, ebp; pop ebp
mov esp, ebp; pop ebp
o leave
para salir.
Es por eso que es muy importante entender para un programador qué es un marco de pila cuando se trata de opciones de compilación, porque el compilador puede controlar si generar este código o no.
En algunos casos, el compilador puede omitir el marco de pila (código de entrada y salida para la rutina), y las variables se accederán directamente a través del puntero de pila (SP / ESP / RSP) en lugar del puntero base conveniente (BP / ESP / RSP). Condiciones para la omisión del marco de pila, por ejemplo:
- la función es una función de hoja (es decir, una entidad final que no llama a otras funciones);
- no hay construcciones try / finally o try / except o similar, es decir, no se utilizan excepciones;
- no se invocan rutinas con parámetros salientes en la pila;
- la función no tiene parámetros;
- la función no tiene un código de ensamblaje en línea;
- etc ...
Omitir marcos de pila (código de entrada y salida para la rutina) puede hacer que el código sea más pequeño y más rápido, pero también puede afectar negativamente la capacidad de los depuradores para rastrear los datos en la pila y mostrarlos al programador. Estas son las opciones del compilador que determinan bajo qué condiciones una función debe tener el código de entrada y salida, por ejemplo: (a) siempre, (b) nunca, (c) cuando sea necesario (especificando las condiciones).
Si entiendes muy bien la pila, entenderás cómo funciona la memoria en el programa y si entiendes cómo funciona la memoria en el programa comprenderás cómo se almacena la función en el programa y si entiendes cómo funciona almacenar en el programa comprenderás cómo funciona la función recursiva y si usted entiende cómo funciona la función recursiva, comprenderá cómo funciona el compilador y si comprende cómo funciona el compilador, su mente funcionará como compilador y depurará cualquier programa con mucha facilidad
Déjame explicarte cómo funciona la pila:
Primero debes saber cómo funciona almacenar en la pila:
Valores de asignación de memoria dinámica de almacenamiento en montón. Pila de almacenamiento automático de valores de asignación y eliminación.
Comprendamos con el ejemplo:
def hello(x):
if x==1:
return "op"
else:
u=1
e=12
s=hello(x-1)
e+=1
print(s)
print(x)
u+=1
return e
hello(4)
Ahora entiendo partes de este programa:
Ahora veamos qué es la pila y cuáles son las piezas de la pila:
Asignación de la pila:
Recuerda una cosa si cualquier función obtiene "return" sin importar si ha cargado todas sus varibles locales o cualquier cosa que regrese inmediatamente de la pila será su stack frame. Significa que cualquier función recursiva obtiene condición base y ponemos return después de condición base para que la condición base no espere cargar variables locales que están situadas en la parte "else" del programa devolverá inmediatamente el frame actual de la pila y ahora si un frame el próximo recuadro regresa al registro de activación. Vea esto en la práctica:
Desasignación del bloque:
Entonces, cada vez que una función encuentra return statement, elimina el frame actual de la pila.
al regresar del valor de la pila, volverá en orden inverso al orden en el que se asignaron en la pila.
Estas son descripciones muy breves y si quieres saber más sobre la pila y la recursión doble, lee dos publicaciones de este blog:
Un marco de pila es un marco de datos que se inserta en la pila. En el caso de una pila de llamadas, una estructura de pila representaría una llamada de función y sus datos de argumento.
Si mal no recuerdo, la dirección de retorno de la función se inserta primero en la pila, luego los argumentos y el espacio para las variables locales. Juntos, hacen el "marco", aunque esto es probablemente dependiente de la arquitectura. El procesador sabe cuántos bytes hay en cada cuadro y mueve el puntero de la pila en consecuencia a medida que los marcos se empujan y salen de la pila.
EDITAR:
Hay una gran diferencia entre las pilas de llamadas de mayor nivel y la pila de llamadas del procesador.
Cuando hablamos de la pila de llamadas de un procesador, estamos hablando de trabajar con direcciones y valores a nivel de byte / palabra en el ensamblado o el código de máquina. Existen "call stacks" cuando se habla de lenguajes de nivel superior, pero son una herramienta de depuración / tiempo de ejecución administrada por el entorno de tiempo de ejecución para que pueda registrar lo que salió mal con su programa (en un nivel alto). En este nivel, a menudo se conocen cosas como los números de línea y los nombres de métodos y clases. Para cuando el procesador obtiene el código, no tiene ningún concepto de estas cosas.
Un resumen rápido. Tal vez alguien tenga una mejor explicación.
Una pila de llamadas se compone de 1 o muchos cuadros de pila. Cada cuadro de pila corresponde a una llamada a una función o procedimiento que aún no ha finalizado con una devolución.
Para utilizar un marco de pila, un hilo mantiene dos punteros, uno se llama puntero de pila (SP) y el otro se llama puntero de marco (FP). SP siempre apunta a la "parte superior" de la pila, y FP siempre apunta a la "parte superior" del marco. Además, el hilo también mantiene un contador de programa (PC) que apunta a la próxima instrucción que se ejecutará.
Los siguientes se almacenan en la pila: variables locales y temporales, parámetros reales de la instrucción actual (procedimiento, función, etc.)
Existen diferentes convenciones de llamadas con respecto a la limpieza de la pila.