Ir al contenido principal

Algunos conceptos importantes de la memoria

Administración de memoria
5.1. Funciones y operaciones
El único espacio de almacenamiento que el procesador puede utilizar di- rectamente, más allá de los registros (que si bien le son internos y sumamente rápidos, son de capacidad demasiado limitada) es la memoria física. Todas las arquitecturas de procesador tienen instrucciones para interactuar con la me- moria, pero ninguna lo tiene para hacerlo con medios persistentes de almacena- miento, como las unidades de disco. Cabe mencionar que cuando se encuentre en un texto referencia al almacenamiento primario siempre se referirá a la me- moria, mientras que el almacenamiento secundario se refiere a los discos u otros medios de almacenamiento persistente.

Espacio de direccionamiento
La memoria está estructurada como un arreglo direccionable de bytes. Esto es, al solicitar el contenido de una dirección específica de memoria, el hardwa- re entregará un byte (8 bits), y no menos. Si se requiere hacer una operación sobre bits específicos, se deberá solicitar y almacenar bytes enteros

Hardware: la unidad de manejo de memoria (MMU)
Con la introducción de sistemas multitarea, es decir, dos o más programas ejecutandose, se vio la necesidad de tener más de un programa cargado en me- moria. Esto conlleva que el sistema operativo junto con información del pro- grama a ejecutar debe resolver cómo ubicar los programas en la memoria física disponible.

La memoria caché
Hay otro elemento en la actualidad que se asume como un hecho: la memo- ria caché. Si bien su manejo es (casi) transparente para el sistema operativo, es muy importante mantenerlo en mente.

La respuesta para reducir esa espera es la memoria caché. Esta es una memo- ria de alta velocidad, situada entre la memoria principal y el procesador propia- mente, que guarda copias de las páginas que van siendo accesadas, partiendo del principio de la localidad de referencia:
Localidad temporal Es probable que un recurso que fue empleado reciente- mente vuelva a emplearse en un futuro cercano.
5.1. FUNCIONES Y OPERACIONES 171
Localidad espacial La probabilidad de que un recurso aún no requerido sea ac- cesado es mucho mayor si fue requerido algún recurso cercano.
Localidad secuencial Un recurso, y muy particularmente la memoria, tiende a ser requerido de forma secuencial.

El espacio en memoria de un proceso
Cuando un sistema operativo inicia un proceso, no se limita a volcar el ar- chivo ejecutable a memoria, sino que tiene que proporcionar la estructura para que éste vaya guardando la información de estado relativa a su ejecución.
Sección (o segmento) de texto Es el nombre que recibe la imagen en memo- ria de las instrucciones a ser ejecutadas. Usualmente, la sección de texto ocupa las direcciones más bajas del espacio en memoria.
Sección de datos Espacio fijo preasignado para las variables globales y datos inicializados (como las cadena de caracteres por ejemplo). Este espacio es fijado en tiempo de compilación, y no puede cambiar (aunque los datos que cargados allí sí cambian en el tiempo de vida del proceso).
Espacio de libres Espacio de memoria que se emplea para la asignación diná- mica de memoria durante la ejecución del proceso. Este espacio se ubica por encima de la sección de datos, y crece hacia arriba. Este espacio es co- nocido en inglés como el Heap.

Resolución de direcciones
Un programa compilado no emplea nombres simbólicos para las variables o funciones que llama;3 el compilador, al convertir el programa a lenguaje má- quina, las sustituye por la dirección en memoria donde se encuentra la variable o la función.4 

Asignación de memoria contigua
En los sistemas de ejecución en lotes, así como en las primeras computado- ras personales, sólo un programa se ejecutaba a la vez. Por lo que, más allá de la carga del programa y la satisfacción de alguna eventual llamada al sistema so- licitando recursos, el sistema operativo no tenía que ocuparse de la asignación de memoria. Al nacer los primeros sistemas operativos multitarea, se hizo necesario re- solver cómo asignar el espacio en memoria a diferentes procesos.

Partición de la memoria
La primer respuesta, claro está, es la más sencilla: asignar a cada programa a ser ejecutado un bloque contiguo de memoria de un tamaño fijo. En tanto los programas permitieran la resolución de direcciones en tiempo de carga o de ejecución, podrían emplear este esquema. 

Fragmentación
Comienza a aparecer cuando más procesos terminan su ejecución, y el sis- tema operativo libera la memoria asignada a cada uno de ellos. A medida que los procesos finalizan, aparecen regiones de memoria disponible, interrumpi- das por regiones de memoria usada por los procesos que aún se encuentran activos. 
Primer ajuste El sistema toma el primer bloque con el tamaño suficiente para alojar el nuevo proceso. Este es el mecanismo más simple de implementar y el de más rápida ejecución. No obstante, esta estrategia puede causar el desperdicio de memoria, si el bloque no es exactamente del tamaño requerido.
Mejor ajuste El sistema busca entre todos los bloques disponibles cuál es el que mejor se ajusta al tamaño requerido por el nuevo proceso. Esto iplica la revisión completa de la lista de bloques, pero permite que los bloques remanentes, una vez que se ubicó al nuevo proceso, sean tan pe- queños como sea posible (esto es, que haya de hecho un mejor ajuste).
Peor ajuste El sistema busca cuál es el bloque más grande disponible, y se lo asigna al nuevo proceso. Empleando una estrucura de datos como un montículo, esta operación puede ser incluso más rápida que la de primer espacio. Con este mecanismo se busca que los bloques que queden des- pués de otorgarlos a un proceso sean tan grandes como sea posible, de cierto modo balanceando su tamaño.

Compactación
Un problema importante que va surgiendo como resultado de esta frag- mentación es que el espacio total libre de memoria puede ser mucho mayor que lo que requiere un nuevo proceso, pero al estar fragmentada en muchos bloques, éste no encontrará una partición contigua donde ser cargado. 

Segmentación
Al desarrollar un programa en un lenguaje de alto nivel, el programador usualmente no se preocupa por la ubicación en la memoria física de los dife- rentes elementos que lo componen. Esto se debe a que en estos lenguajes las variables y funciones son referenciadas por sus nombres, no por su ubicación6. No obstante, cuando se compila el programa para una arquitectura que soporte segmentación, el compilador ubicará a cada una de las secciones presentadas en la sección 5.1.4 en un segmento diferente. Esto permite activar los mecanismos que evitan la escritura accidental de las secciones de memoria del proceso que no se deberían modificar (aquellas que contienen código o de sólo lectura), y permitir la escritura de aquellas que sí (en las cuales se encuentran las variables globales, la pila o stack y el espacio de asignación dinámica o heap). 

Permisos
Una de las principales ventajas del uso de segmentación consiste en per- mitir que cada uno de los segmentos tenga un distinto juego de permisos para el proceso en cuestión: el sistema operativo puede indicar, por ejemplo, que el segmento de texto (el código del programa) sea de lectura y ejecución, mientras que las secciones de datos, libres y pila (donde se almacena y trabaja la infor- mación misma del programa) serán de lectura y escritura, pero la ejecución estará prohibida

Intercambio parcial
Un uso muy común de la segmentación, particularmente en los sistemas de los 1980, era el de permitir que sólo ciertas regiones de un programa sean intercambiadas al disco: si un programa está compuesto por porciones de có- digo que nunca se ejecutarán aproximadamente al mismo tiempo en sucesión, puede separar su texto (e incluso los datos correspondientes) en diferentes seg- mentos. 

Rendimiento
En la sección 5.2.1,dondesepresentaelconceptodeintercambio,seexplicó que intercambiar un proceso completo resultaba demasaido caro. Cuando se tiene de un espacio de memoria segmentado y, muy particularmente, cuando se usan bibliotecas de carga dinámica, la sobrecarga es mucho menor.

Paginación
La fragmentación externa y, por tanto, la necesidad de compactación pue- den evitarse por completo empleando la paginación. Ésta consiste en que cada proceso está dividio en varios bloques de tamaño fijo (más pequeños que los segmentos) llamados páginas, dejando de requerir que la asignación sea de un área contigua de memoria. Claro está, esto requiere de mayor espacialización por parte del hardware, y mayor información relacionada a cada uno de los procesos: no basta sólo con indicar dónde inicia y termina el área de memo- ria de cada proceso, sino que se debe establecer un mapeo entre la ubicación real (física) y la presentada a cada uno de los procesos (lógica). La memoria se presentará a cada proceso como si fuera de su uso exclusivo. 
Cuando se trabaja bajo una arquitectura que maneja paginación, las direc- ciones que maneja el CPU ya no son presentadas de forma absoluta. Los bits de cada dirección se separan en un identificador de página y un desplazamiento, de forma similar a lo presentado al hablar de resolución de instrucciones en tiempo de ejecución. La principal diferencia con lo entonces abordado es que cada proceso tendrá ya no un único espacio en memoria, sino una multitud de páginas. 

Tamaño de la página
Ahora, si bien la fragmentación externa se resuelve al emplear paginación, el problema de la fragmentación interna persiste: al dividir la memoria en blo- ques de longitud preestablecida de 2n bytes, un proceso en promedio desper- diciará 2n 2 (y, en el peor de los casos, hasta 2n − 1). Multiplicando esto por el número de procesos que están en ejecución en todo momento en el sistema, para evitar que una proporción sensible de la memoria se pierda en fragmen- tación interna, se podría tomar como estrategia emplear un tamaño de página tan pequeño como fuera posible.

 Almacenamiento de la tabla de páginas
Algunosdelosprimerosequiposenmanejarmemoriapaginadaempleaban un conjunto especial de registros para representar la tabla de páginas. Esto era posible dado que eran sistemas de 16 bits, con páginas de 8 KB (213). Esto significa que en el sistema había únicamente ocho páginas posibles (23), por lo que resultaba sensato dedicar un registro a cada una. En los equipos actuales, mantener la tabla de páginas en registros resultaría claramente imposible: teniendo un procesador de 32 bits, e incluso si se defi- niera un tamaño de página muy grande (por ejemplo, 4 MB), existirían 1 024 páginas posibles;9 con un tamaño de páginas mucho más común (4 KB, 212 by- tes), la tabla de páginas llega a ocupar 5 MB.10 Los registros son muy rápidos, sin embargo, son en correspondencia muy caros. El manejo de páginas más pequeñas (que es lo normal), y muy especialmente el uso de espacios de direc- cionamiento de 64 bits, harían prohibitivo este enfoque. 

El buffer de traducción adelantada (TLB)
La salida obvia a este dilema es el uso de un caché. Sin embargo, más que un caché genérico, la MMU utiliza un caché especializado en el tipo de infor- mación que maneja: el buffer de traducción adelantada o anticipada. (Translation lookaside buffer. El TLB es una tabla asociativa (un hash) en memoria de alta ve- locidad, una suerte de registros residentes dentro de la MMU, donde las llaves son las páginas y los valores son los marcos correspondientes. De este modo, las búsquedas se efectúan en tiempo constante. 

Subdividiendo la tabla de páginas
Incluso empleando un TLB, el espacio empleado por las páginas sigue sien- do demasiado grande. Si se considera un escenario más frecuente que el pro- puesto anteriormente: empleando un procesador con espacio de direcciona- miento de 32 bits, y un tamaño de página estándar (4 KB, 212), se tendría 1 048 576 (220) páginas. Si cada entrada de la página ocupa 40 bits13 (esto es, cinco bytes), cada proceso requeriría de 5 MB (cinco bytes por cada una de las páginas) sólamente para representar su mapeo de memoria. Esto, especial- mente en procesos pequeños, resultaría más gravoso para el sistema que los beneficios obtenidos de la paginación.

Subdividiendo la tabla de páginas
Incluso empleando un TLB, el espacio empleado por las páginas sigue sien- do demasiado grande. Si se considera un escenario más frecuente que el pro- puesto anteriormente: empleando un procesador con espacio de direcciona- miento de 32 bits, y un tamaño de página estándar (4 KB, 212), se tendría 1 048 576 (220) páginas. Si cada entrada de la página ocupa 40 bits13 (esto es, cinco bytes), cada proceso requeriría de 5 MB (cinco bytes por cada una de las páginas) sólamente para representar su mapeo de memoria. Esto, especial- mente en procesos pequeños, resultaría más gravoso para el sistema que los beneficios obtenidos de la paginación.

 Memoria compartida
Hay muchos escenarios en que diferentes procesos pueden beneficiarse de compartir áreas de sumemoria. Uno de ellos es como mecanismode comunicaciónentreprocesos(IPC,interprocesscommunication),enquedosomásprocesos pueden intercambiar estructuras de datos complejas sin incurrir en el costo de copiado que implicaría copiarlas por medio del sistema operativo. Otro caso, mucho más frecuente, es el de compartir código. Si un mismo pro- grama es ejecutado varias veces, y dicho programa no emplea mecanismos de código auto-modificable, no tiene sentido que las páginas en que se representa ca- da una de dichas instancias ocupe un marco independiente —el sistema opera- tivo puede asignar a páginas de diversos procesos el mismo conjunto de marcos, con lo cual puede aumentar la capacidad percibida de memoria. 

Memoria virtual
Varios de los aspectos mencionados en la sección 5.4 (paginación) van con- formando a lo que se conoce como memoria virtual: en un sistema que emplea paginación, un proceso no conoce su dirección en memoria relativa a otros pro- cesos, sino que trabajan con una idealización de la memoria, en la cual ocupan el espacio completo de direccionamiento, desde el cero hasta el límite lógico de la arquitectura, independientemente del tamaño físico de la memoria disponible.

Paginación sobre demanda
La memoria virtual entra en juego desde la carga misma del proceso. Se debe considerar que hay una gran cantidad de código durmiente o inalcanzable: aquel que sólo se emplea eventualmente, como el que responde ante una situa- ción de excepción o el que se emplea sólo ante circunstancias particulares (por ejemplo, la exportación de un documento a determinados formatos, o la veri- ficación de que no haya tareas pendientes antes de cerrar un programa). Y si bien a una computadora le sería imposible ejecutar código que no esté cargado en memoria,17 éste sí puede comenzar a ejecutarse sin estar completamente en memoria: basta con haber cargado la página donde están las instrucciones que permiten continuar con su ejecución actual. 

Rendimiento
La paginación sobre demanda puede impactar fuertemente el rendimiento de un proceso -se ha explicado ya que un acceso a disco es varios miles de ve- ces más lento que el acceso a memoria. Es posible calcular el tiempo de acceso efectivo a memoria (te) a partir de la probabilidad que en un proceso se presen- te un fallo de página (0 ≤ p ≤ 1), conociendo el tiempo de acceso a memoria (ta) y el tiempo que toma atender a un fallo de página

Reemplazo de páginas
Si se aprovechan las características de la memoria virtual para aumentar el grado de multiprogramación, como se explicó en la sección anterior, se presen- ta un problema: al sobre-comprometer memoria, en determinado momento, los procesos que están en ejecución pueden caer en un patrón que requiera car- garse a memoria física páginas por un mayor uso de memoria que el que hay físicamente disponible. Y si se tiene en cuenta que uno de los objetivos del sistema operativo es otorgar a los usuarios la ilusión de una computadora dedicada a sus procesos, no sería aceptable terminar la ejecución de un proceso ya aceptado y cuyos requisitos han sido aprobados, porque no hay suficiente memoria. Se vuelve necesario encontrar una forma justa y adecuada de llevar a cabo un reemplazo de páginas que permita continuar satisfaciendo sus necesidades. El reemplazo de páginas es una parte fundamental de la paginación, ya que es la pieza que posibilita una verdadera separación entre memoria lógica y física. El mecanismo básico a ejecutar es simple: si todos los marcos están ocupados, el sistema deberá encontrar una página que pueda liberar (una pá- gina víctima) y grabarla al espacio de intercambio en el disco. Luego, se puede emplear el espacio recién liberado para cargar la página requerida, y continuar con la ejecución del proceso.

 Hiperpaginación
Es un fenómeno que se puede presentar por varias razones: cuando (bajo un esquema de reemplazo local) un proceso tiene asignadas pocas páginas pa- ra llevar a cabo su trabajo, y genera fallos de página con tal frecuencia que le imposibilita realizar trabajo real. Bajo un esquema de reemplazo global, cuan- do hay demasiados procesos en ejecución en el sistema y los constantes fallos y reemplazos hacen imposible a todos los procesos involucrados avanzar, tam- bién se presenta hiperpaginación.23 Hay varios escenarios que pueden desencadenar la hiperpaginación, y su efecto es tan claro e identificable que prácticamente cualquier usuario de cómputo lo sabrá reconocer. A continuación se presentará un escenario ejem- plo en que las malas decisiones del sistema operativo pueden conducirlo a este estado. Suponga un sistema que está con una carga media normal, con un esquema de reemplazo global de marcos. Se lanza un nuevo proceso, que como parte de su inicialización requiere poblar diversas estructuras a lo largo de su espacio de memoria virtual. Para hacerlo, lanza una serie de fallos de página, a las que el sistema operativo responde reemplazando a varios marcos pertenecientes a otros procesos. 

Consideraciones de seguridad
Para una cobertura a mayor profundidad del material presentado en esta sección, se sugiere estudiar los siguientes textos:
Smashing The Stack For Fun And Profit (Aleph One 1996)
The Tao of Buffer Overflows (Sánchez Inédito)


Desbordamientos de buffer (buffer overflows)
Una de las funciones principales de los sistemas operativos en la que se ha insistido a lo largo del libro es la de implementar protección entre los proce- sos pertenecientes a diferentes usuarios, o ejecutándose con distinto nivel de privilegios. Y si bien el enfoque general que se ha propuesto es el de analizar por separado subsistema por subsistema, al hablar de administración de me- moria es necesario mencionar también las implicaciones de seguridad que del presente tema se pueden desprender. En las computadoras de arquitectura von Neumann, todo dato a ser proce- sado (sean instrucciones o datos) debe pasar por la memoria, por el almacena- miento primario. Sólo desde ahí puede el procesador leer la información direc- tamente. 

Las bibliotecas y la seguridad
El ligado dinámico puede traer consigo una serie de problemas, entre los cuales se destacan el manejo de versiones de las bibliotecas y potenciales vul- nerabilidades. El primer problema es conocido, en ambientes Windows, como el infierno de las DLL. éste se puede causar de muchas formas. Por ejemplo, si al instalar un nuevo programa, se instala también una versión incompatible de una biblioteca que es usada por otros programas. Esto causa que los demás programas no se puedan ejecutar, y lo que es más, hace que la depuración del fallo sea muy difícil. Por otro lado, si no se tienen los controles suficientes, al desinstalar un programa se puede borrar una biblioteca compartida, lo cual puede llevar a que otros programas dejen de funcionar. El infiero de las DLL puede ser prevenido mediante estrategias como el ver- sionamiento de las biblioteca de ligado dinámico (esto es, hacer que cada com- ponente de las bibliotecas lleve la versión que implementa o nivel de compatibi- lidad que implementa),28 y mediante el uso de scripts de instalación o gestores de dependencias que verifican si en el sistema hay una versión compatible de la biblioteca. Teniendo esta información, la biblioteca en cuestión se instalará únicamente en caso necesario.




Comentarios

Entradas populares de este blog

Elementos del Estado Mapa Conceptual

Conceptos básicos de electrónica Román Alzate

En electrónica cuando hay una corriente fluyendo organizadamente se le conoce como corriente eléctrica. Si no conectamos algo de donde sale la corriente, esta no va a salir, si lo comparamos con una llave de agua los dispositivos que necesiten corriente son los que determinan si la corriente sale mucha o poca, a esta medición se le llama voltaje que es la diferencia de potencial que tiene entre la salida de la corriente y la absorción de esta. La potencia es otro concepto que consiste en la cantidad de corriente que es emitida en un tiempo determinado. En el caso del conductor eléctrico que es como el tubo que soporta la capacidad de la corriente para que esta fluya a través de él que es la resistencia, la resistencia es otro concepto importante. Aquí tenemos los cuatro conceptos fundamentales tanto de electrónica como de electricidad: Voltaje, Corriente, Potencia y Resistencia.   Estos conceptos tienen medidas diferentes: Potencia se mide en Vatios. W El voltaje ...

Presentación Inducción Electrónica Básica

Hasta el griego debemos remontarnos para conocer el origen etimológico de la palabra electrónica. En concreto, podemos determinar que procede de la unión de dos partes léxicas claramente diferenciadas: elektron que se traduce como “ámbar” y el sufijo –iko que viene a significar “relativo a”.  Electrónica Se conoce como electrónica al análisis de los electrones y a la aplicación de sus principios en diferentes contextos. Puede decirse, por lo tanto, que la noción de electrónica refiere a lo que está vinculado con el electrón, que es una de las partículas esenciales de los átomos. La ingeniería y la física se encargan del desarrollo y el análisis de los sistemas creados a partir del movimiento y el control de electrones que tienen una carga de electricidad.  Los denominados circuitos electrónicos posibilitan la conversión y la distribución de la energía eléctrica, por lo que se pueden emplear en el procesamiento y el control de información. A nivel general puede decirse que ...