820 likes | 1.01k Views
Administración de Almacenamiento. Tania Pérez Miguel Angel Mendoza. ¿Por qué y para quién es importante?. El almacenamiento es un recurso escaso en los sistemas de cómputo. La administración de almacenamiento es una inquietud general tanto para los: Programadores Implementadores y
E N D
Administración de Almacenamiento Tania Pérez Miguel Angel Mendoza
¿Por qué y para quién es importante? El almacenamiento es un recurso escaso en los sistemas de cómputo. La administración de almacenamiento es una inquietud general tanto para los: • Programadores • Implementadores y • Diseñadores de lenguajes.
Diseñadores Los lenguajes contienen muchas restricciones o características que pueden ser explicadas solamente por el simple deseo de los diseñadores en permitir una u otra técnica en la administración del almacenamiento. Por ejemplo, FORTRAN no permite llamadas a subprogramas de manera recursiva e hipotéticamente, si esto fuera posible, no habría algún cambio en su sintaxis pero su implementación requeriría de una estructura dinámica que administrara el almacenamiento en tiempo de ejecución. Pascal esta cuidadosamente diseñado para permitir que la administración del almacenamiento se realice dinámicamente a través de un stack. LISP por ejemplo, permite la recolección de basura.
Implementadores Cada lenguaje permite el uso de ciertas técnicas en la administración de almacenamiento, los detalles en los mecanismos y su representación en hardware y software, son las tareas del implementador. Por ejemplo, el diseño de LISP se enfoca en la recolección de basura y el uso de una lista de espacio libre como las bases de la administración de almacenamiento, pero existen una gran cantidad de técnicas que solucionan esta necesidad; así que el implementador deberá escoger la técnica mas apropiada para el HW y el SW disponible.
Programadores Es de suma importancia que los programas usen de manera eficiente el almacenamiento disponible, esto significa que los programadores deberán tener un control directo (aunque mínimo, para evitar interferencias con los administradores de almacenamiento controlados por el sistema) sobre la administración del almacenamiento. Por ejemplo, PL/1 permite al usuario cierto control en la administración del almacenamiento sobre sus propias estructuras de datos con las operaciones: ALLOCATE y FREE. PASCAL hace lo propio con las operaciones NEW y DISPOSE.
Principales elementos de datos, programas y operaciones que requieren almacenamiento en tiempo de ejecución • Segmentos de código que se usan en la traducción de los programas del usuario. • Programas del sistema que soportan la ejecución de los programas del usuario. • Estructuras de datos y constantes definidas por el usuario. • Puntos de regreso de subprogramas. • Entornos de referencia • Asignaciones temporales en evaluación de expresiones. • Asignaciones temporales en el paso de parámetros. • Buffers de entrada y salida. • Datos propios del sistema. • Llamadas a subprogramas y retorno de operaciones. • Operaciones como: creación y destrucción de estructuras de datos. • Operaciones como: inserción y borrado de componentes.
Principales elementos que requieren espacio en tiempo de ejecución • Segmentos de código para los programas de usuario. Una gran parte de los bloques de almacenamiento en cualquier sistema debe ser reservada para guardar los segmentos de código que representarán la traducción de los programas de usuario.
Principales elementos que requieren espacio en tiempo de ejecución • Programas del sistema en tiempo de ejecución Otra parte sustancial del bloque de almacenamiento durante la ejecución debe ser reservada para los programas de sistema que soporten la ejecución de los programas del usuario. Estas pueden ser desde simples rutinas de librerías, por ejemplo de funciones matemáticas o funciones que impriman cadenas hasta intérpretes o traductores de software presentes durante la ejecución.
Principales elementos que requieren espacio en tiempo de ejecución • Estructuras de datos y constantes definidas por el usuario Debe de existir espacio disponible para las estructuras que fueron creadas o declaradas por el usuario, incluyendo las constantes.
Principales elementos que requieren espacio en tiempo de ejecución • Puntos de regreso de subprogramas Como los subprogramas tienen la propiedad de ser invocados desde cualquier parte del programa, debe reservarse memoria para el control de secuencia generada internamente, como los puntos de retorno de estos.
Principales elementos que requieren espacio en tiempo de ejecución • Entornos de referencia Conocidos como asociaciones de identificadores. Estos pueden requerir de una gran cantidad de espacio, por ejemplo una lista A en LISP (más adelante se verá lo que es esto).
Principales elementos que requieren espacio en tiempo de ejecución • Asignaciones temporales en evaluación de expresiones. Se requiere un espacio de almacenamiento temporal para los resultados intermedios de una evaluación. Por ejemplo, en la evaluación de la expresión (x + y) * (u + v), el resultado de la primera suma debe ser almacenado temporalmente mientras el resto de la evaluación se completa.
Principales elementos que requieren espacio en tiempo de ejecución • Asignaciones temporales en el paso de parámetros Cuando un subprograma es llamado, una lista de los parámetros en turno debe de ser evaluada y los resultados almacenados temporalmente hasta que la evaluación de la lista se complete. Cuando la evaluación de algún parámetro de la lista requiera llamadas a funciones recursivas, existe la posibilidad de que se emplee un número ilimitado de almacenamientos temporales sucesivos.
Principales elementos que requieren espacio en tiempo de ejecución • Buffers de entrada y salida (Registros bidireccionales) Los buffers sirven como áreas de almacenamiento temporal donde los datos son guardados durante el tiempo de transferencia física hacia el almacenamiento externo, incluso, es posible efectuar operaciones con sus datos. Generalmente cientos de localidades son reservadas para los registros, mas si se trata de arquitecturas tipo RISC.
Principales elementos que requieren espacio en tiempo de ejecución • Datos propios del sistema En casi todos los lenguajes, se requiere almacenamiento para diversos datos propios del sistema, por ejemplo: la información sobre el status de los registros, estados de banderas, bits contadores de referencia o de recolección de basura.
Principales elementos que requieren espacio en tiempo de ejecución Además de los elementos antes mencionados, es necesario considerar las principales operaciones que requieren almacenamiento en memoria: • Llamadas a subprogramas y retorno de operaciones. Las llamadas a subprogramas, entornos de referencia y otros datos utilizados en subprogramas son la mayoría de operaciones que requieren almacenamiento. La ejecución del retorno desde un subprograma, usualmente requiere la liberación del espacio de almacenamiento utilizado durante su ejecución (información definida por la liga dinámica).
Principales elementos que requieren espacio en tiempo de ejecución • Operaciones como:creación y destrucción de estructuras de datos. Si el lenguaje posee operaciones que permiten la creación de nuevas estructuras de datos en puntos arbitrarios durante la ejecución de un programa (no solamente cuando inicia algún subprograma), entonces estas operaciones normalmente requieren disponer de espacio aparte del utilizado cuando inicia el subprograma. Ejemplos de esto son las instrucciones new en pascal o malloc en C. Además el lenguaje debe proveer una operación explícita de destrucción como dispose en pascal o free en C.
Principales elementos que requieren espacio en tiempo de ejecución • Operaciones como: inserción y borrado de componentes Si el lenguaje proporciona operaciones de inserción y borrado en una estructura de datos, es necesario definir operaciones de almacenamiento y liberación del espacio para que estas operaciones puedan ser implementadas.
¿Cómo y cuándo se asignan los espacios de almacenamiento? Inicialmente. Asignación definida antes de la ejecución de un programa. Recuperando espacios. Asignación definida en ejecución. Reusando espacios.
Fases del manejo de almacenamiento • Asignación inicial Al principio de la ejecución cada pieza de almacenamiento debera estar: reservada o disponible. Si inicialmente resulta libre, entonces estará disponible para poder ser reservada dinámicamente durante la ejecución del programa. Cualquier sistema de almacenamiento requiere de alguna técnica para mantener un registro del espacio libre, así como mecanismos para liberar o asignar este espacio libre dependiendo de las necesidades durante la ejecución.
Fases del manejo de almacenamiento • Recuperación El almacenamiento que ha sido asignado y usado subsecuentemente debería de ser recuperado por el administrador de almacenamiento para su reuso. El proceso de recuperación puede ser simple, como el reposicionamiento del apuntador de pila, o complejo, como la recuperación de “basura”.
Fases del manejo de almacenamiento • Compactación y reuso. En el mejor de los casos, el espacio de almacenamiento recuperado podría estar inmediatamente listo para su reúso. Otras veces podría ser necesaria la compactación, que es la construcción de grandes bloques libres a partir de piezas pequeñas de almacenamiento. El reúso normalmente involucra las mismas técnicas que la asignación inicial.
Tipos de Administración del Alamacenamiento • Estático. Asignación que permanece fija durante toda la ejecución. • Dinámico • Stack • Heap • Elementos de Tamaño fijo • Elementos de Tamaño Variable
Manejo de almacenamiento estático • Es la manera más sencilla de asignación. El espacio reservado durante la traducción del programa permanece fijo durante la ejecución. No requiere almacenamiento en tiempo de ejecución y por supuesto tampoco provee recuperación y reuso. • Normalmente el almacenamiento de segmentos de código del usuario y programas del sistema (así como las microinstrucciones) son almacenados estaticamente. • El manejo de almacenamiento estático es eficiente, ya que no se requiere de tiempo ni de espacio para la administración de almacenamiento durante la ejecución. • Para muchos programas la asignación estática es bastante satisfactoria. FORTRAN y COBOL están diseñados para almacenamiento estático.
Manejo de almacenamiento basado en el stack. • Es la forma más simple de administración de almacenamiento en tiempo de ejecución. El espacio libre al inicio de la ejecución consiste en un bloque secuencial de la memoria. Conforme un nuevo espacio es solicitado, este es tomado de localidades sucesivas de este bloque, comenzando por algún extremo. El espacio debe de ser liberado en orden inverso, para que el bloque de espacio liberado quede siempre en la parte alta de la pila. • Basicamente lo que se necesita para controlar este tipo de almacenamiento es un stack pointer. Este siempre apuntará a la siguiente localidad disponible del stack. • El mecanismo de compactación es automático. • Esta es una estructura de llamadas de subprogramas y returnos anidados del tipo: último en entrar - primero en salir.
Manejo de almacenamiento basado en el stack. PASCAL La mayoría de las implementaciones de Pascal están basadas en una pila central de registros de activación de subprogramas, junto con un área estáticamente reservada que contiene los programas del sistema y los segmentos de código de subprogramas.
Manejo de almacenamiento basado en el stack. Organización de la memoria en Pascal en tiempo de ejecución
Manejo de almacenamiento basado en el stack. LISP Las llamadas a subprogramas son estrictamente anidadas y también el stack es usado para los registros de activación. Cadaregistro de activación contiene un punto de retorno (liga dinámica) y un espacio temporal para la evaluación de expresiones y el paso de parámetros. Pero las referencias a entornos locales son guardadas en un stack separado representado como una lista de ligas, llamada A-list.
Manejo de almacenamiento basado en el stack Organización de la memoria en LISP durante laejecución
Para colocar un arreglo en el área de heap Los programadores necesitan hacer esto cuando se obtienen algún error del tipo: “existen muchas variables” o “se esta fuera del rango disponible para datos”. La solución de esto es colocar la estructura de dato al área de heap. En PASCAL, esto se logra usando un apuntador al arreglo y la definición del arreglo se haría en la sección de TYPE, mas no en la sección de VAR. Si se escribe un enunciado del tipo: var MyArray: array[0..100] of Char entonces, se estará asignando espacio de memoria en el segmento de datos, que es lo que se quiere evitar. La solución es definir el arreglo en la sección TYPE y declararlo en la sección VAR. var MyArray: PMyArray; donde PMyArray es un apuntador a un arreglo.
Uses Dos, Crt; Type PMyArray = ^TMyArray; { PMyArray es un apuntador a estructuras del tipo TMyArray } TMyArray = array[0..999] of LongInt; { TMyArray es un arreglo de 1000 elementos } procedure Pause; begin FlushKeyBuffer; { Este es un procedure que se asegurar que el buffer de las teclas esté vacío }ReadKey; { Pausa… } end; Var MyAry: PMyArray; { MyAry es un apuntador al arreglo TMyArray} i: Integer; begin ClrScr; New(MyAry); { Asigna espacio de memoria en el heap, el tamaño asignado corresponderá al tamaño de la estructura TMyArray } for i := 0 to 999 do { Llena el arreglo } MyAry^[i] := i; for i := 500 to 510 do { Escribe algunos elementos del arreglo en la pantalla } WriteLn(MyAry^[i]); Pause; Dispose(MyAry); { Desaloja la memoria } end. Recordar siempre desalojar la memoria asignada con NEW!!!
Administración de almacenamiento por heap: Elementos de tamaño fijo. • El tercer tipo de manejo de almacenamiento es el llamado almacenamiento por heap. Un heap es un bloque de almacenamiento con piezas que son asignadas y liberadas de una manera relativamente estructurada. Aquí los problemas de reservación de espacio, recuperación, compactación y nuevo uso pueden ser difíciles de resolver.
Administración de almacenamiento por heap: Elementos de tamaño fijo. • La necesidad de utilizar un heap surge cuando un lenguaje permite almacenamiento para asignación y liberación en puntos arbitrarios durante la ejecución de un programa.
ML • Por ejemplo en ML dos listas pueden concatenarse para la creación de una tercera fun append (nil, l) = l | append (h::t, l) = h :: append (t, l)
ML • El programador puede definir dinámicamente un nuevo tipo de datos. Se activan los mecanismos necesarios Compiler.Control.Lazy.enabled := true;open Lazy; • Se define un tipo de streams: • datatype lazy 'a stream = Cons of 'a * 'a stream • La palabra reservada "lazy" indica que los valores de tipo'a streamson cálculos pendientes, que al ser efectuados, generan un valor de la formaCons (x, c), con x un valor de tipo'a, yces otro valor de tipo'a stream, i.e., otro cálculo que depende del primer valor
LISP • Se puede agregar un nuevo elemento a una estructura de lista ya existente en cualquier punto. • Tambien se puede liberar espacio en puntos impredecibles durante la ejecución.
LISP • > (append '(1 2 3) '(4 5 6)) ; concatenate lists (1 2 3 4 5 6) • > (intersection '(a b c) '(b)) ; set intersection (B) • > (union '(a) '(b)) ; set union (A B) • > (set-difference '(a b) '(a)) ; set difference
Almacenamiento por Heap • Hay que gestionar el almacenamiento dinámico de: • Elementos de Tamaño fijo • Elementos de Tamaño variable
Elementos de tamaño fijo • Unelemento almacenado en el heap y después liberado ocupa N palabras de la memoria. Generalmente N debe de ser 1 o 2. Suponiendo que el heap ocupa un bloques contiguos de la memoria, conceptualmente podemos dividirlo en una sucesión de K elementos, cada uno de longitud N, así que el tamaño del heap es N*K.
Elementos de tamaño fijo • Inicialmente los K elementos están ligados para formar una lista de espacio libre. Esto quiere decir que la primera palabra de cada elemento en la lista apunta a la primera palabra del siguiente elemento en la lista. Para reservar un elemento, el primer elemento en la lista de espacio libre es removido de la lista y su apuntador es retornado a la operación que solicitó el almacenamiento
Elementos de tamaño fijo. Cuando un elemento es liberado, simplemente es ligado a la cabeza de la lista de espacio libre.
Recuperación: Contadores de referencia y recolección de basura • La forma más sencilla de recuperación es la del retorno explícito. Cuando un elemento que estaba en uso está disponible para ser usado nuevamente, este debe ser explícitamente identificado como free y retornado a la lista de espacio libre. • Cuando los elementos son utilizados por el sistema, cada rutina del sistema es responsable de liberar el espacio.
Recuperación • El retorno explícito es la técnica natural para administrar el almacenamiento en el heap, pero desafortunadamente no siempre es posible emplearla. Hay dos problemas que la inutizan: garbage y dangling references.
Recuperación • dangling references: si una estructura se destruye antes de que todos los accesos a la estructura hayan sido destruidos, cualquier trayectoria restante se convierte en una referencia dangling. • Garbage: si el último acceso a la estructura es destruido sin que la estructura misma sea destruida, entonces la estructura se convierte en garbage.
Recuperación • El retorno explícito de heaps facilita la creación de garbage y dangling references. • Ejemplos en C: Garbage: int *p, *q; /* p y q son apuntadores a enteros*/ ... p=malloc(sizeof(int)); /* se almacena un entero */ p = q; /* se pierde la dirección de p */
Recuperación • dangling: int *p, *q; /* p y q son apuntadores a enteros */ ... p=malloc(sizeof(int)); /* se almacena un entero */ q = p; /* se copia a q la dirección de p */ free(p); /* se libero el espacio apuntado por q */
Recuperación En el contexto de la administración de almacenamiento en heap: • una referencia dangling es un apuntador a un elemento que ha sido retornado a la lista de espacio libre, y cuyo espacio podría ser utilizado para cualquier otro propósito. • Un elemento garbage es uno que está disponible para su reuso, pero que como no se encuentra en la lista de espacio libre, se convierte en espacio inaccesible
LISP • El sistema enfrenta las mismas dificultades que el usuario. En LISP. Por ejemplo, las listas ligadas son un tipo de estructura de datos básico. Una de las operaciones primitivas de LISP es cdr, la cual, dado un apuntador a un elemento en la lista ligada, retorna un apuntador al siguiente elemento de la lista
LISP • El siguiente elemento al que fue originalmente apuntado podría haber sido ser liberado por la operación cdr, si el único apuntador hacia este es el determinado por cdr . Si cdr no retorna el elemento a la lista de espacio libre, este se convierte en basura.