Documentos de Académico
Documentos de Profesional
Documentos de Cultura
La memoria una gran matriz de palabras o bytes, cada uno con su propia dirección. La CPU extrae instrucciones de la memoria según
el valor del contador de programa. Estas instrucciones pueden hacer que se carguen valores de direcciones de memoria específicas
o que se guarden en ellas.
Un ciclo de ejecución de una instrucción primero extrae una instrucción de la memoria. Luego, la instrucción se decodifica y se
extraen de memoria una serie de operandos si la instrucción lo necesita. Después de haber ejecutado la instrucción con esos
operandos, se almacenen los resultados en la memoria. La unidad de memoria sólo ve un flujo de direcciones de memoria y no sabe
cómo se generan esas direcciones (mediante el contador de programa, mediante indexación, indirección, direcciones literales, etc.)
ni tampoco para qué se utilizan (instrucciones o datos).
Vinculación de direcciones
Usualmente, los programas residen en un disco como archivos binarios ejecutables. Para poder ejecutarse, un programa deberá ser
cargado en memoria y colocado dentro de un proceso.
Dependiendo del mecanismo de gestión de memoria que se utilice, el proceso puede desplazarse entre disco y memoria durante su
ejecución. Los procesos del disco que estén esperando a ser cargados en memoria para su ejecución forman la cola de entrada.
El procedimiento normal es seleccionar uno de los procesos de la cola de entrada y cargarlo en la memoria. A medida que se ejecuta
el proceso, accede a las instrucciones y datos de la memoria. Cuando el proceso terminará su ejecución, su espacio de memoria
queda disponible.
El programa de usuario debe pasar por varias etapas antes de ejecutarse. En cada etapa, las direcciones se representan de diferentes
formas. Las direcciones del programa fuente son simbólicas. Un compilador vincula las direcciones simbólicas con direcciones
reubicables. El editor de montaje o cargador vincula las direcciones reubicables con direcciones absolutas. Cada vinculación es una
transformación de un espacio de direcciones a otro.
La vinculación de las instrucciones y los datos con direcciones de memoria se pueden hacer en cualquiera de las etapas:
Tiempo de compilación: Si al momento de compilar se sabe en qué parte de la memoria va a residir el proceso, se genera un código
absoluto. Si luego la posición inicial cambia, se deberá recompilar el código.
Tiempo de carga: Si al momento de compilar no se sabe en qué parte de la memoria va a residir el proceso, se genera un código
reubicable. En este caso, la vinculación final se retrasa hasta el momento de la carga. Si la dirección inicial cambia, sólo se volverá a
cargar el código de usuario para incorporar el valor modificado.
Tiempo de ejecución: Si, durante su ejecución, un proceso puede desplazarse de un segmento de memoria a otro, la vinculación se
retarda hasta el momento de la ejecución. Para que este esquema funcione se necesita de hardware especial.
Carga dinámica
Es un mecanismo que se utiliza para obtener una mejor utilización del espacio de memoria. Con esta técnica, una rutina no se carga
hasta que se la invoca; todas las rutinas se mantienen en disco en un formato de carga reubicable. El programa principal se carga en
la memoria y se ejecuta. Cuando una rutina necesita llamar a otra, primero comprueba si la otra ya se cargó. si no es así, se invoca
el cargador de enlace reubicable para que cargue la rutina deseada en memoria y actualice las tablas de direcciones del programa
para reflejar este cambio. Después, se pasa el control a la rutina recién cargada.
VENTAJAS: si una rutina no se utiliza no se carga en memoria. Este método es útil cuando se necesitan grandes cantidades de código
para gestionar casos que sólo ocurren de manera infrecuente, como por ejemplo rutinas de error. Aunque el tamaño total del
programa pueda ser grande, la porción que se utilice (y que por tanto se cargue) puede ser mucho más pequeña.
La carga dinámica no requiere soporte especial por parte del sistema operativo. Los usuarios diseñan sus programas para poder
aprovechar este método.
Podemos seleccionar entre varios métodos distintos para establecer esta correspondencia, como veremos en las Secciones 8.3 a
8.7. Por el momento, vamos a ilustrar esta operación de asociación mediante un esquema MMU simple, que es una generalización
del esquema de registro base descrita en la Sección 8.1. El registro base se denominará ahora registro de reubicación. El valor
contenido en el registro de reubicación suma a todas las direcciones generadas por un proceso de usuario en el momento de
enviarlas a memoria (véase la Figura 8.4.).
Por ejemplo, si la base se encuentra en la dirección 14000, cualquier intento del usuario de direccionar la posición de memoria cero
se reubicará dinámicamente en la dirección 14000; un acceso a la ubicación 346 se convertirá en la ubicación 14346. El sistema
operativo MS-DOS que se ejecuta sobre la familia de procesadores Intel 80x86 utiliza cuatro registros de reubicación a la hora de
cargar y ejecutar procesos.
El programa de usuario nunca ve las direcciones físicas reales. El programa puede crear un puntero a la ubicación 346, almacenarlo
en memoria, manipularlo y compararlo con otras direcciones, siempre como el número 346. Sólo cuando se lo utiliza como dirección
de memoria (por ejemplo, en una operación de lectura o escritura indirecta) se producirá la reubicación en relación con el registro
base. El programa de usuario maneja direcciones lógicas y el hardware de conversión (mapeo) de memoria convierte esas
direcciones lógicas en direcciones físicas. Esta forma de acoplamiento en tiempo de ejecución ya fue expuesta en la Sección 8.1.2.
La ubicación final de una dirección de memoria referenciada no se determina hasta que se realiza esa referencia.
Ahora tenemos dos tipos diferentes de direcciones: direcciones lógicas (en el rango comprendido entre 0 y max) y direcciones físicas
(en el rango comprendido entre R + 0 y R + max para un valor base igual a R). El usuario sólo genera direcciones lógicas y piensa que
el proceso se ejecuta en las ubicaciones comprendidas entre 0 y max. El programa de usuario suministra direcciones lógicas y estas
direcciones lógicas deben ser convertidas en direcciones físicas antes de utilizarlas.
El concepto de un espacio de direcciones lógicas que se acopla a un espacio de direcciones físicas separado resulta crucial para una
adecuada gestión de la memoria.
8.2 Intercambio
Un proceso debe estar en memoria para ejecutarse. Pero, los procesos pueden intercambiarse temporalmente de la memoria a un
almacenamiento auxiliar, y luego devueltos a la memoria para continuar su ejecución. Por ejemplo, si se utiliza un entorno de
multiprogramación con un algoritmo de planificación de CPU basado en turnos. Cuando termina un cuantum de tiempo, el
administrador de memoria intercambia a disco el proceso que acaba de terminar y a cargar en el espacio de memoria liberado por
otro proceso (Figura 8.5).
Mientras tanto, el planificador de la CPU asigna un cuantum de tiempo a otro proceso que ya esté en la memoria. Cuando un proceso
termina su cuantum, se intercambia con otro proceso.
El administrador de memoria puede intercambiar procesos con la suficiente rapidez como para que siempre haya procesos en
memoria, listos para ejecutarse, cuando el planificador de la CPU desea asignar el procesador a otra tarea. Además, el cuantum
debe ser lo suficientemente grande como para que pueda realizarse una cantidad razonable de cálculos entre una operación de
intercambios y la siguiente.
En los algoritmos de planificación por prioridad se utiliza una variante de esta política de intercambio llamada rodar hacia fuera,
rodar hacia dentro: cuando un proceso de mayor prioridad llega y desea ser servicio, el administrador de memoria puede
intercambiar a disco el proceso de menor prioridad y, cargar y ejecutar el de mayor prioridad. Cuando el proceso de mayor prioridad
termina, el proceso de menor prioridad puede intercambiarse de nuevo a la memoria y continuar su ejecución.
Normalmente, un proceso que se intercambia a disco se volverá a cargar en el mismo espacio de memoria que ocupaba
anteriormente. Esta restricción depende el método de vinculación de las direcciones. La vinculación debe hacerse en tiempo de
ejecución para que el proceso pueda transferirse a una posición de memoria distinta ya que las direcciones se calculan durante la
ejecución; si la vinculación se hace en tiempo de carga, el proceso no puede transferirse.
El intercambio necesita un almacenamiento auxiliar, que debe ser rápido y debe tener el tamaño suficiente para almacenar las
copias de todas las imágenes de memoria para todos los usuarios, y debe proporcionar un acceso directo a estas imágenes de
memoria.
El sistema mantiene una cola de procesos listos que consiste en todos los procesos cuyas imágenes de memoria están en el almacén
auxiliar o en la memoria y que están listos para ejecutarse. Cada vez que el planificador de la CPU decide ejecutar un proceso, llama
al despachador, que comprueba si el siguiente proceso de la cola está en la memoria; Si no es así, y si no hay una región de memoria
libre, el despachador intercambia el proceso deseado por otro proceso que esté actualmente en memoria. A continuación, recarga
los registros y transfiere el control al proceso seleccionado.
Para un uso eficiente de la CPU, es necesario que el tiempo de ejecución de cada proceso sea mayor que el tiempo de intercambio.
La mayor parte del tiempo de intercambio es tiempo de transferencia. El tiempo de transferencia total es directamente proporcional
a la cantidad de memoria intercambiada.
Es útil conocer exactamente cuánta memoria está utilizando un proceso de usuario, y no simplemente cuánta podría estar utilizando.
Así, sólo sería necesario intercambiar lo que estuviera utilizándose realmente, reduciendo así el tiempo de intercambio. Para que
este método sea efectivo, el usuario debe mantener informado al sistema de cualquier cambio que se produzca en las necesidades
de memoria. Así, un proceso con requisitos de memoria dinámicos necesitará ejecutar llamadas al sistema (request memory y
release memory) para informar al sistema operativo de sus cambiantes necesidades de memoria.
El intercambio está restringido también por otros factores: Para intercambiar un proceso, esté debe estar inactivo. En este sentido,
es necesario prestar una atención las operaciones de E/S pendientes. Un proceso puede estar esperando por una operación de E/S
en el momento en que queramos intercambiarlo con el fin de liberar memoria. En ese caso, si la E/S está accediendo a la memoria
de usuario donde residen los búferes de E/S, el proceso no podrá ser intercambiado.
Antes de seguir analizando la cuestión de la asignación de memoria, debemos hablar del tema de la conversión (mapping, mapeo)
de memoria y la protección. Podemos proporcionar estas caracte¬rísticas utilizando un registro de reubicación, como se explica en
la Sección 8.1.3, con un registro límite, como hemos visto en la Sección 8.1.1. El registro de reubicación contiene el valor de la
dirección física más pequeña, mientras que el registro límite contiene el rango de las direcciones lógicas (por ejemplo, reubicación
= 100040 y límite = 74600). Con los registros de reubicación y de límite, cada dirección lógica debe ser inferior al valor contenido en
el registro límite; la MMU con¬vertirá la dirección lógica dinámicamente sumándole el valor contenido en el registro de
reubica¬ción. Esta dirección es la que se envía a la memoria (Figura 8.6).
Cuando el planificador de la CPU selecciona un proceso para su ejecución, el despachador carga en los registros de reubicación y de
límite los valores correctos, como parte del proceso de cambio de contexto. Puesto que todas las direcciones generadas por la CPU
se comparan con estos registros, este mecanismo nos permite proteger tanto al sistema operativo como a los programas
La fragmentación de memoria puede ser también interna, además de externa. Considere un esquema de asignación de particiones
múltiples con un agujero de 18464 bvtes. Suponga que el
siguiente proceso solicita 18462 bytes; si asignamos exactamente el bloque solicitado, nos queda¬rá un agujero de 2 bytes. El
espacio de memoria adicional requerido para llevar el control de este agujero será sustancialmente mayor que el propio agujero.
La técnica consiste en descomponer la memoria física en bloques de tamaño fijo y asignar la memoria en unidades basadas en el
tamaño de bloque. Con esta técnica, la memoria asignada a un proceso puede ser ligeramente superior a la memoria solicitada. La
diferencia entre los dos valores será la fragmentación interna, es decir, la memoria que es interna a una partición pero que no está
siendo utilizada.
Una solución al problema de la fragmentación externa es la compactación. El objetivo es mover el contenido de la memoria con el
fin de tener toda la memoria libre junta, para formar un único bloque grande. Sin embargo, la compactación no siempre es posible.
Si la reubicación es estática y se hace en tiempo de ensamblado o de carga, no puede haber compactación; la compactación sólo es
posible si la reubicación es dinámica y se lleva a cabo en tiempo de ejecución. Si las direcciones se reubican dinámicamente, esto
sólo requiere cambiar de lugar el programa y los datos y luego cambiar el registro base para reflejar la nueva dirección base utilizada.
Si la compactación es posible, hay que determinar su costo. El algoritmo de compactación más simple consiste en mover todos los
procesos hacia uno de los extremos de la memoria; de esta forma, todos los agujeros se moverán en la otra dirección, produciendo
un agujero grande de memoria disponible. Sin embargo, este esquema puede ser muy caro de implementar.
Otra posible solución al problema de la fragmentación externa consiste en permitir que el espacio de direcciones lógicas de los
procesos no sea contiguo, lo que hace que podamos asignar memoria física a un proceso con independencia de dónde esté situada
dicha memoria. Hay dos técnicas complementarias que permiten implementar esta solución: la paginación (Sección 8.4) y la
segmentación (Sección 8.6). Asimismo, estas técnicas pueden también combinarse (Sección 8.7).
Paginación
Es otra solución al problema de fragmentación externa que permite que el espacio de direcciones físicas de un proceso no sea
contiguo. La paginación evita el problema de ajustar los trozos de memoria de tamaño variable en el almacenamiento auxiliar;
Cada dirección generada por la CPU se divide en dos partes: un número de página (p) y un desplazamiento de página (d). El número
de página se utiliza como índice para una tabla de páginas, la cual contiene la dirección base de cada página en memoria física; esta
dirección base se combina con el desplazamiento de página para definir la dirección de memoria física que se envía a la unidad de
memoria.
El tamaño de página (al igual que el tamaño de marco) está definido por el hardware, y suele ser una potencia de 2, variando entre
512 bytes y 16 MB por página, dependiendo de la arquitectura de la computadora. La selección de una potencia de 2 como tamaño
de página facilita la traducción de una dirección lógica a un número de página y desplazamiento de página.
Si el tamaño del espacio de direcciones lógicas es 2" y el tamaño de página es 2" unidades de direccionamiento (bytes o palabras),
entonces los m — n bits de mayor peso de cada dirección lógica designarán el número de página, mientras que los n bits de menor
peso indicarán el desplazamiento de página. Por tanto, la dirección lógica tiene la estructura siguiente:
El esquema de paginación es una forma de reubicación dinámica. Cada dirección lógica es asignada por el hardware de paginación
a alguna dirección física. La utilización de la paginación es similar al uso de una tabla de registros base (o de reubicación), uno por
cada marco de memoria.
Cuando usamos un esquema de paginación, no tenemos fragmentación externa: todos los marcos libres podrán ser asignados a un
proceso que los necesite. Sin embargo, lo que puede haber es algo de fragmentación interna, ya que los marcos se asignan como
unidades. si los requisitos de memoria de un proceso no coinciden con las fronteras de página, el último marco asignado puede no
estar completamente lleno.
Si el tamaño de los procesos es independiente del tamaño de las páginas, la fragmentación interna podría ser, como promedio, igual
a media página por cada proceso. Por ello, conviene utilizar página pequeñas. Cuando llega un proceso al sistema para ejecutarlo,
se examina su tamaño expresado en páginas. Cada página del proceso necesitará un marco. Por tanto, si el proceso requiere n
páginas, deberá haber disponibles al menos n marcos en memoria. Si los hay, se los asignará al proceso entrante. La primera página
del proceso se carga en uno de los marcos asignados y se incluye el número de marco en la tabla de páginas para este proceso, y así
sucesivamente.
Un aspecto importante de la paginación es diferencia entre la visión de la memoria que tiene el usuario y la memoria física real. El
programa de usuario ve la memoria como un único espacio que sólo contiene ese programa. La diferencia entre la visión de la
memoria que tiene el usuario y la memoria física real se resuelve mediante el hardware de traducción de direcciones. Las direcciones
lógicas se traducen a direcciones físicas y esta conversión es hecha por el sistema operativo.
Como el sistema operativo gestiona la memoria física, debe ser consciente de los detalles de la asignación de la memoria física: qué
marcos han sido asignados, qué marcos están disponibles, cuál es el número total de marcos, etc. Esta información se guarda en
una estructura de datos llamada tabla de marcos, cual tiene una entrada por cada marco físico que indica si está libre o asignado y,
en caso de estar asignado, a qué página de qué proceso o procesos ha sido asignado.
Además, el sistema operativo debe ser consciente de que los procesos de usuario operan en el espacio de usuario y de que todas
las direcciones lógicas deben ser convertidas con el fin de generar direcciones físicas.
8.4.3 Protección
La protección de la memoria en un entorno paginado se hace mediante bits de protección asociados a cada marco. Estos bits se
mantienen en la tabla de páginas. Un bit define si una página es de lectura-escritura o de sólo lectura. Cada referencia a memoria
pasa a través de la tabla de páginas para encontrar el número de marco correcto. Mientras se calcula la dirección física, se examinan
los bits de protección para verificar que no se esté haciendo ninguna escritura en una página de sólo lectura. Si esto sucede
provocará una interrupción hardware al sistema operativo (o una violación de protección de memoria).
Esta estrategia de protección se puede ampliar fácilmente para proporcionar un nivel más fino de protección. Se Puede crear un
hardware que proporcione protección de sólo lectura, de lectura-escritura o de sólo ejecución. También, se puede permitir cualquier
combinación de estos accesos, si se incluyen bits de protección individuales para cada tipo de acceso. Los intentos no validos
causaran una interrupción de hardware hacia el sistema operativo.
Generalmente, se añade un bit más a cada entrada de la tabla de páginas: un válido-inválido. Cuando este bit indica "válido", la
página asociada se encontrará el espacio de direcciones lógicas del proceso y será una página válida. Cuando este bit indica
"inválido", la página no se encuentra en el espacio de direcciones lógicas del proceso. Las direcciones inválidas se capturan utilizando
el bit válido-inválido. El sistema operativo configura este bit para cada página, con el fin de permitir o prohibir el acceso a dicha
página.