Mapa de memoria de un programa imperativo en la mayoría de los procesadores

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Prefacio

La programación imperativa no es tan misteriosa o intimidante como suena. La mayoría de los lenguajes de programación populares modernos tienen características imperativas, y el programador medio utiliza construcciones imperativas todo el tiempo. La programación imperativa simplemente significa que le estamos diciendo a la computadora qué hacer en cada paso. En esencia, cada declaración de programación que hacemos cambia algún tipo de estado, normalmente memoria, dentro de la computadora. Wikipedia lo destila así:

En la informática, la programación imperativa es un paradigma de programación que usa declaraciones que cambian el estado de un programa. De la misma manera que el estado de ánimo imperativo en los lenguajes naturales expresa órdenes, un programa imperativo consiste en órdenes que la computadora debe realizar. La programación imperativa se centra en describir cómo funciona un programa.

Casi todos los equipos informáticos tradicionales, con más procesadores de enfoque, es imprescindible. El procesador lee una serie de unos y ceros y lo interpreta como un comando para hacer algo, ya sea para almacenar un valor en la memoria, agregar dos valores juntos o comparar dos valores entre sí. Esa es la razón por la cual muchos lenguajes de programación, particularmente lenguajes de programación anteriores tales como ensamblador y C, son también imperativos en naturaleza pues son realmente abstracciones del hardware subyacente.

Por eso, cuando hablo de un “mapa de memoria” de un “programa imperativo” en la mayoría de los procesadores, realmente estoy tratando de referirme a programas construidos “cerca del hardware”, como en un lenguaje como ensamblador o C. Más Procesadores y sistemas operativos procesarán y ejecutarán programas con este tipo de modelo de memoria, desde DOS, Windows, Linux, etc. No todos los procesadores o sistemas operativos hacen esto, pero es una buena apuesta si estás programando algo como C, Usted va a funcionar en este tipo de modelo. Ahora, cuando hablo de un “mapa de memoria” estoy indicando lo que se utilizan varias porciones de memoria de la computadora, como RAM o disco, mientras el programa se ejecuta. Imagínese como un poco de un atlas a una tierra o un paisaje en particular, estoy diciendo que esto es lo que se encuentra aquí y lo que se utiliza. El primer lugar en el que debemos detenernos es el procesador, ya que tiene algunas piezas de memoria disponibles para él que considero importante conocer.

El Procesador

Un procesador típico en estos días, construido alrededor de un modelo imperativo, tiene un número de lo que se conoce como registros. Usted puede pensar en un registro de un procesador como una especie de pequeña unidad de almacenamiento cableado en el procesador. Me imagino que es como una estantería que coloca valores binarios dentro y fuera. Los registros pueden asumir diferentes tamaños y roles particularmente diferentes. Generalmente hay registros de uso general que un programa puede usar libremente para realizar sus operaciones, pero también hay registros que indican varias piezas de operación general y algunas veces son establecidos por el propio procesador. Por ejemplo, puede haber un registro de contador de programa que retiene la dirección de memoria de la siguiente instrucción para que el procesador se desplace. Puede haber un registro de pila que indique la dirección de memoria de la posición actual en la pila (llegaremos a una pila más tarde), puede haber un registro que contiene varias banderas o bits que indican que ciertas condiciones se cumplen después de ciertas instrucciones. Los registros pueden variar de 8 bits de largo, a 128 bits de largo o más, todo depende del hardware. La clave aquí es que el direccionamiento de un registro es diferente de dirigir un punto en la memoria fuera del procesador tal como RAM.

De hecho, a menudo sólo se puede tener acceso directo a los registros en un idioma como el montaje. En un programa de montaje habitual, se indican los registros y partes de los registros con diferentes mnemónicos e identificadores. No tienen una dirección normal como una variable podría decir en RAM. Los registros son los valores más rápidos para un procesador de acceso, viendo cómo están “en” el procesador ellos mismos, y por lo general, incurrir en muy poco tiempo de costo en funcionamiento. Hay palabras clave como registrarse en el lenguaje C, que supuestamente hace que una variable sea accesible en un registro, pero todo depende realmente del compilador. Cualquier lenguaje más “abstracto” o nivel más alto que C normalmente no permite el acceso a registros en absoluto.

Algunos procesadores tienen cachés que como pequeños segmentos de memoria que viven sólo “al lado” por así decir de los internos del procesador. Éstos son como registros y son más fáciles y más rápidos de tener acceso que la memoria exterior tal como RAM. La mayoría de las veces el procesador, y tal vez el sistema operativo, se aprovecha de estas cachés de varias maneras, aunque algunos permiten el acceso del programador. En la programación general, no vas a acceder a estos cachés directamente, a menos que estés realmente tratando de sacar tanto control y rendimiento fuera del procesador como sea posible.

Las Cuatro Regiones Generales de la Memoria

En muchos sistemas operativos, la “memoria de trabajo” de un programa puede dividirse entre RAM y acceso a disco, aunque el programador que programa el programa (whew) no sabe y no sabe dónde se produce la división. Para él, hay cuatro regiones de memoria de trabajo que seguramente se supone que están en RAM que un programa imperativo se aprovecharía de. Éstos son el código del programa, las variables / datos globales, el montón y la pila. He trazado un diagrama de cómo estas cuatro áreas de la memoria y cómo se pueden relacionar entre sí en el abstracto:

Código de programa es donde reside el código de máquina (o código de objeto) de su programa. Aquí es donde se almacenan las representaciones binarias de las sentencias ejecutables que se están ejecutando actualmente. Si está utilizando un intérprete en lugar de compilar sus programas, el intérprete sería almacenado y ejecutado desde esta área, tomando su programa como entrada. Sin embargo, si compiló su programa hasta el código de la máquina, todas las instrucciones del código de máquina se alinearían aquí para que el procesador pasara. Es posible almacenar datos en la sección de código de programa, normalmente se hace después del código del programa en sí, pero no es exactamente el método más sólido de almacenamiento de datos. Hay muchas alternativas al almacenamiento de datos, la mayoría usando archivos separados.

Variables / datos globales es donde se almacenan todos los valores de las variables globales o datos globales, como puede ser el caso. Esto es importante porque una variable global almacenada aquí tendrá una dirección fija de memoria de conjunto. La razón por la que es importante tener un área separada para las variables globales y los datos es que, imagínese si el valor de una variable global se movía en la memoria. En un momento fue en esta dirección, pero luego sucedió algo en el programa y fue trasladado a otra dirección. ¿Cómo sabría alguna de las otras partes del programa dónde acceder a la variable global a partir de entonces? Tendrías que tener una ubicación fija almacenando la dirección de esta variable global flotante que cada parte del programa podría confiar en … y si vas a tener eso, ¿por qué no sólo almacenar la variable global en esa ubicación fija ? Por lo tanto, tiene las variables globales / área de datos del mapa de memoria.

Voy a ir un poco fuera de orden y hablar primero de la pila. La pila es un tipo de datos abstracto tradicional muy conocido de la informática (incluso hago una implementación en mi biblioteca de phabstractic). Básicamente, la idea es que usted puede poner un valor “en” una pila, y luego más tarde tomar ese valor “fuera” de la pila. En esencia, se ponen los valores en la pila, uno “encima” de los demás, y luego pop cada uno de la pila en orden inverso para recuperar sus valores. Esta es una construcción muy útil por varias razones, una de las cuales las elaboraré, pero primero necesitamos saber qué tipos de valores se almacenan en la pila. La pila se utiliza en muchos programas imperativos para una amplia gama de cosas, incluyendo el almacenamiento de las direcciones de retorno de las llamadas de función, los valores de los argumentos a las funciones, las variables locales de la función, e incluso el estado actual de la CPU si es necesario. Si no está seguro de las funciones, argumentos y variables locales, consulte el artículo de Mi curso de programación (Bootstrap Part 1). Ahora bien, la razón por la que es importante que la pila funciona de la manera que lo hace es debido a cómo funcionan las funciones o subrutinas (en el montaje).

Imagine que está en el programa principal, y tiene un conjunto de variables. A continuación, se llama a una función con algunos valores. Bueno, ¿quieres recordar cuáles son tus variables locales en este momento cuando regresas de la función / subrutina derecha? Por lo tanto, colóquelos en la pila, y luego cuando la función / subrutina regresa, pop de la pila. Bueno, ahora estás en esa función / subrutina … pero llamas a otra función / subrutina. El mismo trato, almacenar sus variables locales y argumentos y tal en la pila, colocándolos en la parte superior de las variables anteriores. Cuando esa nueva función / subrutina vuelve, sólo salen de sus variables … cuando regrese de la función que, pop de las principales variables de programas y voila. En esencia, la pila le permite anidar las llamadas de función en la parte superior de las llamadas de función y tal, de modo que, al empezar a ir en sentido inverso, puede recuperar todos ellos de nuevo.

A medida que vaya más lejos en un programa, la pila crecerá y se reducirá en consecuencia. Aquí es donde la línea y el separador punteado vienen para el montón. Muchas veces a medida que crece la pila crecerá hacia arriba / hacia abajo en el espacio que el montón (estamos llegando a él) ocupa. Si la pila se queda sin espacio o interfiere con el montón tenemos lo que llamamos un desbordamiento de pila. Esto puede suceder si llamamos a muchas funciones dentro de funciones dentro de funciones, etc. Esto es particularmente un problema en lo que se conoce como funciones recursivas, o en esencia funciones que se llaman a sí mismas, colocando información en la pila cada vez.

El montón es el área de memoria dada al programa para el resto de sus datos volátiles. Si necesita inicializar un bloque de memoria, por ejemplo, para una matriz o una imagen, debería llamar a las funciones de asignación / desasignación de memoria de su lenguaje para reservar algún espacio en esta área de memoria y cargar todos esos datos. Datos para valores grandes y complejos de variables tales como, como se dijo, imágenes o archivos de sonido. Sin embargo, también tiene valores más pequeños. En algunos sistemas operativos el montón se mueve alrededor y se desplaza para acomodar varios valores sin el conocimiento del programador, que no tiene que preocuparse por tales cosas. Sin embargo, como se indicó anteriormente, es posible que se quede sin espacio en el montón y se ejecute en la pila. En lugar de ser un “desbordamiento de montón” esto a menudo se llama simplemente quedarse sin memoria.

Conclusión

Este no es el único mapa de memoria de cada programa en todas partes, pero es generalmente el mapa de memoria para la mayoría de los programas que la mayoría de los programadores promedio crean, siendo imperativo. En lenguajes de programación como ensamblador o C, se trabaja con este mapa de forma bastante directa, pero en lenguajes más abstractos o de nivel superior como Python o PHP, muchos de estos problemas y ubicaciones de memoria son atendidos por el intérprete o el compilador. Esa es la belleza de la abstracción continua, puede usar lenguajes de bajo nivel para ocuparse de varios problemas tediosos, como la gestión de la memoria, de una manera automática que le permite pasar a temas más embriagadores como, ¿cómo funciona este programa loco !? Sin embargo, no importa qué nivel o enfoque de la programación que está trabajando, es importante conocer los problemas de memoria y el paisaje que puede estar afectando a su programa, y espero que este artículo ayudó a elaborar los problemas de hoy, como el desbordamiento de la pila. ¡Gracias por leer!

Esto es parte de una serie más grande conocida como “Cómo Programar Cualquier Cosa: Core Rulebook

Si usted aprecia este artículo usted puede ser que considere el apoyar de mi Patreon.

Pero si un compromiso mensual es un poco más, lo entiendo, podría considerar comprarme un café.

photo credit: ” fragments ‘pictosophiques ” 2/3 – To-Be & Not-To-Be are in a boat. Whe(re)n’s the boat the question ? Whe(re)n’s the Styx Memory ? via photopin (license)

También te podría gustar...

Deja un comentario

A %d blogueros les gusta esto: