2. GENERALIDADES
Para ejecutar un proceso, éste debe encontrarse en memoria principal. Como
hemos visto, para mejorar el aprovechamiento de la CPU, ésta se reparte entre
varios procesos, por lo tanto, también habrá que repartir la memoria principal. A la
parte del sistema operativo que se ocupa de gestionar la memoria se le denomina
Gestor de Memoria. Su cometido consiste en llevar la cuenta de las partes de
memoria que se están utilizando y las que están libres, así como de gestionar el
trasvase de información entre la memoria principal y la secundaria cuando la
memoria RAM no sea suficientemente grande para acoger a todos los procesos
3. INTRODUCCION Y OBJETIVOS
En la evolución de la arquitectura de los computadores, la cantidad de memoria principal ha
ido creciendo. Pero el tamaño de los programas crece aún más rápido que la memoria
disponible. El primer intento para resolver el problema de la limitación de memoria fue la
utilización de una técnica denominada OVERLAYS (solapamientos). Un programa con
Solapamientos se dividía en varias partes. Una de estas partes siempre estaba presente en
memoria principal y controlaba la carga sucesiva de las demás partes (OVERLAYS) desde la
memoria secundaria a la principal. Estas otras partes se cargaban en memoria de tal forma
que ocupaban la misma región de memoria en la que había residido otra parte previamente
utilizada, sobre escribiendo así (solapando) a la parte anterior. Posteriormente, la necesidad
de repartir la memoria entre varios usuarios redujo la cantidad de memoria para cada uno,
e hizo necesaria la introducción de algún mecanismo de protección para aislar entre sí las
actividades de los programas
4. OBJETIVOS
Si varios procesos comparten la memoria principal, se debe asegurar
que ninguno de ellos pueda modificar posiciones de memoria de otro
proceso. Aunque la escritura de memoria tiene efectos más
desastrosos, la lectura de memoria ajena tampoco debe estar
permitida, pues cada proceso debe mantener su privacidad.
El compartimiento de la memoria parece estar en contradicción con la
protección, pero es que a menudo también es necesario que varios
procesos puedan compartir y actualizar estructuras de datos comunes,
por ejemplo, en un sistema de bases de datos ; compartir zonas de
código, por ejemplo, en rutinas de biblioteca, para no tener en
memoria distintas copias de la misma rutina.
La multiprogramación requiere que varios procesos residan
simultáneamente en memoria. Lo que no se puede saber antes de
llevarlo a memoria es la dirección absoluta en la que se va a cargar el
proceso, por lo que no es práctico utilizar direcciones absolutas en el
programa. En su lugar, es preferible realizar direccionamientos
relativos para permitir que un programa pueda ser cargado y
ejecutado en cualquier parte de la memoria
Debido al coste de la rápida memoria RAM, normalmente se necesita
ampliarla con memoria secundaria más barata (y más lenta),
utilizando para ello dispositivos tales como discos o cintas
magnéticas. Por el contrario, también puede resultar conveniente
añadir memoria de acceso más rápido que la RAM principal, como es
el caso de la memoria caché, en la que se mantienen los datos de
acceso más frecuente.
Las memorias están organizadas como único espacio lineal de
direcciones secuenciales que van desde 0 hasta n esto no refleja la
estructura lógica de un programa (módulos, rutinas o
procedimientos, etc),. El gestor puede proporcionar varios espacios
de direcciones y así cada estructura lógica podría ser un segmento.
5. GESTION DE MEMORIA SIN INTERCAMBIO
El esquema de memoria más simple consiste en mantener la
memoria ocupada con un único proceso. Cuando se carga un
programa que se hace cargo de toda la memoria y del
control completo de la máquina, se dice que el programa se
carga sobre una MAQUINA DESNUDA, es decir, una
máquina en la que solamente se ofrece el hardware puro,
sin ninguna ayuda software que lo recubra. Las máquinas
desnudas se utilizaron de forma general hasta principios de
los años 60, Más tarde, a las máquinas desnudas se les
añadió una especie de sistema operativo muy rudimentario
al que se le denominó MONITOR. Las funciones que ofrecía
el monitor eran las estrictamente necesarias para cargar un
programa en memoria y controlar su ejecución, todo de
forma muy manual y con una completa supervisión del
usuario. La técnica actual más utilizada en los pequeños
ordenadores es la que se muestra en la parte inferior. La
memoria está ocupada por el sistema operativo, que suele
estar en RAM, y por el cargador inicial del sistema
operativo(IPL) y los DRIVERS de dispositivos, que suelen
estar en memoria ROM. El resto de la memoria RAM queda
disponible como área de usuario
6. Se hace necesario considerar el problema de cómo asignar
memoria disponible a varios de los procesos que están en la
cola de espera para ser traídos a memoria principal. Lo más
inmediato y simple es dividir la memoria en n particiones
(posiblemente de distinto tamaño), de tal forma que encada
partición se mete un proceso, donde permanece hasta que
finaliza su ejecución. Una vez terminado el proceso, la
partición queda libre para acoger a un nuevo trabajo. Con la
llegada de la multiprogramación y este esquema de
memoria aparecen algunos problemas y cuestiones que
deben solventarse, como la planificación de procesos a largo
plazo, la determinación del número y tamaño de las
particiones, la ubicación de los programas y la protección de
las particiones de memoria
7. Un esquema posible para la planificación de procesos a largo plazo,
o sea, para seleccionar los procesos que van cargarse en memoria
para ser ejecutados, puede consistir en que cada una de las
particiones tenga una cola asociada, detal manera que en cada una
de ellas se van encolando los trabajos o procesos dependiendo del
espacio de memoria requerido. Cuando hay que cargar un trabajo,
se le pone en la cola de entrada de la partición más pequeña en la
que quepa. Ya que en este esquema las particiones son de tamaño
fijo prestablecido, cualquier espacio de una partición no utilizado
por el proceso cargado, se desaprovecha. La desventaja de meter
en colas los trabajos según su tamaño se hace patente cuando la
cola de una partición grande está vacía, pero la cola de una
partición pequeña tiene muchos trabajos. Una solución consiste en
tener una única. Cuando una partición se queda libre, el primer
trabajo dela cola que quepa en esa partición, se carga en ella y se
ejecuta. Ya que no es deseable malgastar una partición grande con
un trabajo pequeño, una alternativa puede ser el recorrer la cola
entera y elegir el trabajo más grande que se pueda cargar en la
partición que acaba de quedar libre. No obstante, esto supone una
discriminación de los trabajos con pocos requisitos de memoria,
que no son merecedores de tener una partición grande, mientras
que normalmente a los trabajos pequeños se les suele dar el mejor
servicio, no el peor. Para solucionarlo, se suele disponer siempre de
alguna partición de poco tamaño, para permitir que los pequeños
trabajos también se ejecuten sin necesidad de asignarles grandes
particiones de memoria. No obstante, debe tenerse en cuenta que
un proceso que requiera poca memoria y mucha CPU puede
entonces formar una larga cola de espera por su partición
8. TAMAÑO DE LAS PARTICIONES
Para lograr el mayor aprovechamiento de la
memoria los procesos deberían cargarse en
particiones cuyo tamaño se ajuste lo más
posible al del proceso. Por una parte
tenemos que si dividimos la memoria del
sistema en muchas particiones pequeñas,
puede dar lugar a que algunos programas
grandes no puedan cargarse en memoria
aunque haya suficiente memoria disponible,
si ésta no se encuentra adyacente en una
única partición, dando lugar, en este caso, a
una FRAGAMENTACION EXTERNA de la
memoria. Si por el contrario, se dispone de
unas pocas particiones grandes, los
programas pequeños desaprovecharán la
memoria sobrante de la partición que
ocupen, lo que da lugar a una
FRAGMENTACION INTERNA
.
9. Cuando se monta o enlaza un programa compuesto por diferentes
módulos, todos ellos se combinan en un único módulo cargable, en
el que las referencias a sus objetos locales (rutinas o datos) son
direcciones que van desde cero hasta la correspondiente al tamaño
del módulo. Así, si el módulo se carga en la dirección cero de
memoria principal, se ejecutará correctamente, pero no si se carga
en cualquier otra dirección. Según esto, habría que decirle al
montador en qué dirección se va a cargar ese programa.
Supongamos que la primera instrucción de un programa es una
llamada a un procedimiento en la dirección 100 dentro del fichero
binario producido por el montador. Si este programa se carga en la
partición 1, esa instrucción saltará a la dirección absoluta 100, la
cual, muy posiblemente, corresponderá a un área del sistema
operativo. Lo que se necesita es que la instrucción salte a la
dirección Base_Partición + 100. Así, siempre se realizará la llamada
correcta, independientemente de la partición en la que se cargue el
programa. Una posible solución consiste en modificar las
direcciones del programa a medida que se carga en memoria. O
sea, que a los programas que se carguen en la partición 1 se les
añadirá
Base_Partición_1 a todas las direcciones a las que haga
referencia, Base_Partición_2 a los que se carguen en la partición
2, etc.; siendo la base de la partición, su dirección de comienzo.
Para realizar esto, el montador debe incluir en el módulo cargable
una lista con las direcciones relativas.
Con esta solució n se consiguen programas ESTATICAMENTE
REUBICABLES
(reubicables en tiempo de carga), pero tienen un inconveniente:
No permite que un programa cargado en una partición pueda
moverse a otra distinta antes de finalizar su ejecución, lo cual
resulta de gran utilidad, como veremos posteriormente
10. REUBICACION
Este problema se solventan con un poco de ayuda del
hardware,. Consiste en equipar al procesador con un nuevo
registro: el REGISTRO BASE . Cuando un proceso es
seleccionado para pasar a ejecución, el registro base se
carga con la dirección de la partición que contiene al
proceso en cuestión. Una vez que el proceso está en
ejecución, a cada referencia a memoria se le añade
automáticamente el contenido del registro base, generando
así la dirección definitiva, sin necesidad de modificar el
contenido del módulo ejecutable, ni en la carga ni durante la
ejecución. A este sistema en el que ya sí se permite ejecutar
programas en cualquier partición de memoria e incluso
cambiarlos de partición durante su ejecución, se le conoce
como sistema de REUBICACION DINAMICA
11. La organización de la memoria en particiones fijas
resulta simple y efectiva. Siempre que se puedan
mantener en memoria suficientes trabajos como para
mantener la CPU ocupada
En un sistema de tiempo compartido la situación es
diferente, pues normalmente hay más usuarios que
memoria para contener sus procesos, por lo que se
hace necesario llevar a disco los procesos en exceso.
Cuando al proceso en ejecución se le acabe su porción
de tiempo, es posible que se le vuelva a llevar a
memoria secundaria para dejar espacio al proceso que
haya seleccionado el planificador. Al cambio de
procesos que se llevan de memoria a disco y de disco a
memoria se le denomina INTERCAMBIO (swapping) .
En la cola de Preparados se mantienen todos BCP ’s de
los procesos dispuestos a ejecutarse (cuyas imágenes
de memoria se encuentran en memoria principal o
secundaria). Cuando el planificador elige un proceso
para pasar a ejecución, llama al dispatcher, el cual, a su
vez, comprueba si el proceso seleccionado se
encuentra en memoria principal o no. Si no lo está y no
hay memoria libre, el distpacher
saca aun proceso de memoria a disco y trae a memoria
el proceso seleccionado. Seguidamente realiza el
cambio de contexto
12. Está claro que el tiempo de cambio de contexto en
un sistema con intercambio es bastante alto. Para
obtener un buen aprovechamiento de la CPU, la
porción de tiempo de los procesos deberá ser alto
en comparación con el tiempo dedicado al cambio
completo de proceso, es decir, incluyendo el tiempo
de intercambio. Pero por otra parte, tenemos que
en un sistema de tiempo compartido si la porción de
tiempo es muy alta, el tiempo de espera de los
procesos preparados sube mucho, lo cual no es en
absoluto deseable. Por lo tanto, lo que se debe
conseguir es minimizarlo más posible el tiempo
de intercambio. Para ello hay dos estrategias claras
1. Utilizar una memoria secundaria muy rápida.
2. Intercambiar solamente la memoria necesaria
El segundo quiere decir que si en un sistema hay un
área de usuario de 900 Kbytes, por ejemplo, y hay
que sacar un proceso que ocupa 100 Kbytes,
no hay por qué mover a disco un área de 900Kbytes,
sino solamente los 100 Kbytes ocupados, ya que el
tiempo de intercambio es prácticamente
proporcional al tiempo de transferencia, que a su
vez es proporcional al tamaño del bloque que se lee
o escribe en el disco.
13. Cuando un proceso es sacado a disco, y posteriormente se
le vuelve a traer a memoria principal, es posible que no se
le instale en la misma partición de memoria que ocupaba
la última vez que estuvo en RAM. Esto puede traer
complicaciones si el proceso está realizando operaciones
de E/S. Supongamos que un proceso en ejecución realiza
una operación de E/S, con lo que el sistema operativo lo
pone en espera y lo expulsa a memoria secundaria para
liberar su área de memoria. Sin embargo, es posible que el
dispositivo de E/S acceda asíncronamente a los BUFFERS
que el proceso le indicó, para dejar/recoger los datos a
leer/escribir. Estos BUFFERS se encontraban en el área de
memoria que ocupaba el proceso cuando se estaba
ejecutando, pero cuando pasó a espera y se le expulsó al
disco, este área de memoria pasó a estar ocupada por otro
proceso. Así, es posible que el dispositivo de E/S ahora
esté leyendo o escribiendo en un área de memoria que
pertenece a otro proceso distinto del que solicito la
operación de E/S. Este problema tiene dos soluciones:
1. No sacar nunca de memoria a un proceso que espera
por una operación de E/S.
2. Que los dispositivos de E/S realicen las operaciones
sobre BUFFERS del sistema operativo. De esta
manera, una vez finalizada la operación de E/S
solicitada, cuando el planificador selecciona al
proceso para continuar, se le trae a memoria principal
y se transfiere la información solicitada (si es que era
una lectura) del buffer del sistema al buffer del
proceso. Por último se le cede el control del
procesador.
14. En un sistema de intercambio en particiones de tamaño
fijo, un proceso queda bloqueado en espera, se le
puede mover al disco y traer otro a la partición que deja
libre. Pero este sistema no es aconsejable cuando se
dispone de poca memoria principal, pues los programas
pequeños desperdician mucha memoria cuando ocupan
particiones grandes(fragmentación interna). Otro
enfoque mejor es el basado en particiones de tamaño
variable. Cuando se utilizan particiones variables, el
número y tamaño de los procesos cargados en memoria
varía con el tiempo, como se puede ver . Al principio se
carga el proceso A, seguidamente el B y el C . A
continuación el proceso A termina (o se le expulsa al
disco) y se carga el proceso D ; finaliza el B y se carga el
E.
A medida que se crean procesos, se les pone en una
cola de entrada, anotando sus requisitos de memoria.
Cuando se le asigna memoria a uno de estos procesos,
pasa a la cola de Preparados y puede competir por la
CPU. Cuando un proceso termina libera la memoria
utilizada, con lo que el sistema operativo puede
utilizarla para asignársela a otro proceso de la cola de
entrada. En un momento dado, se tiene una lista de
bloques libres de memoria y sus tamaños. También se
dispone de la cola de entrada de procesos. La memoria
se va asignando a los procesos hasta que no haya un
hueco suficientemente grande para el primer proceso
de la cola de entrada, o bien se busca en la cola un
proceso cuyos requisitos de memoria sean menores y
puedan satisfacerse con alguno de los bloques
disponibles
15. En general siempre hay un conjunto de huecos de distintos
tamaños dispersos por toda la memoria. Cuando un proceso
llega y necesita memoria, se busca en el conjunto de huecos
uno que sea suficientemente grande. Si el hueco es
demasiado grande, se parte en dos: uno se asigna al
proceso, y el otro se devuelve al conjunto de huecos.
Cuando un proceso finaliza, se libera su bloque de memoria,
que se pone de vuelta en el conjunto de huecos. Si este
hueco resulta estar adyacente a otros huecos (a otros
bloques libres), se unen los bloques adyacentes en un único
bloque del tamaño total. Ahora hay que comprobar si hay
más procesos esperando memoria y si este nuevo bloque
liberado y recombinado puede satisfacer las demandas de
cualquiera de estos procesos que están esperando. En algún
momento puede darse el caso de que haya mucha memoria
libre, pero distribuida entre muchos huecos pequeños no
adyacentes. Entonces tenemos la FRAGMENTACION
EXTERNA. La única solución a la fragmentación externa es la
COMPACTACION , que consiste en mover los bloques
asignados, de tal forma que queden adyacentes, dejando
entonces grandes huecos libres que sí pueden asignarse a
procesos.
La compactación es posible gracias a que se utiliza un
sistema de reubicación dinámica, es decir mover el
programa a su nueva posición de memoria y actualizar el
registro base con la nueva dirección de comienzo.
16. CRECIMIENTO DINAMICO DE LA MEMORIA DE UN PROCESO
Consideremos ahora la cantidad de memoria que se le debe asignar a un proceso cuando se le crea o se le lleva a
memoria principal. Si los procesos se crean con un tamaño fijo que nunca cambia, la gestión de memoria es simple: se
asigna exactamente lo que necesita, ni más ni menos. Pero si los procesos pueden crecer, por ejemplo porque realizan
peticiones dinámicas de memoria, se presenta un problema cuando los procesos intentan crecer (necesitan más memoria
de la requerida inicialmente). Si hay un hueco libre adyacente al proceso, se le puede asignar el hueco al proceso y
permitirle expandirse. Pero si el proceso está adyacente a otro proceso, el que pretende expandirse tendrá que moverse a
otro hueco más grande, o bien habrá que sacar algún proceso a disco para dejar espacio libre. Si se espera que los
procesos de un sistema puedan aumentar su tamaño a medida que se ejecutan, es conveniente asignarles inicialmente un
poco de memoria extra, para evitar tener que moverles en caso de que necesite más memoria durante la ejecución.
Si el proceso tiene dos segmentos que pueden crecer, por ejemplo el montículo (heap) y la pila, se suele utilizar la
alternativa b dela Figura, en la que se puede ver que cada proceso tiene una pila en la parte superior de su memoria
creciendo hacia direcciones bajas y, justo debajo, una zona de montículo creciendo hacia las direcciones altas. La
memoria entre estas dos áreas puede asignarse a cualquiera de ellas. Si se acaba este espacio (porque intenten solaparse
la pila y el heap), entonces ya no quedará más remedio que mover el proceso a otro hueco mayor. El área de heap es la
zona de memoria de donde se sirven las peticiones dinámicas de memoria, mientras que la pila se utiliza para ubicar
datos temporales, como por ejemplo: las variables locales, resultados intermedios en la evaluación de expresiones,
parámetros y direcciones de retorno en las llamadas a procedimientos.
17. En general, hay tres métodos utilizados por los sistemas
operativos para llevar la cuenta de la memoria utilizada
y de los huecos libres:
1. Mapa de bits
2. Listas
3. Sistema BUDDY
18. GESTION DE LOS BLOQUES DE MEMORIA
Con este método de gestión de memoria, ésta se divide en
UNIDADES DE ASIGNACION ,cuyo tamaño puede ir desde
unas cuantas palabras a varios Kbytes. Como se puede ver
en cada una de estas unidades de asignación se
corresponde con un bit en el mapa de bits de memoria; la
primera unidad con el primer bit, la segunda con el segundo,
etc., de tal forma que si el bit está a 0 indica que la unidad
de asignación correspondiente se encuentra libre, mientras
que si está a 1 quiere decir que la unidad está ocupada, o
sea, asignada a algún proceso
El tamaño de la unidad de asignación es una cuestión muy importante. Cuanto más pequeña sea la unidad, mayor será
el mapa de bits. No obstante, obsérvese que incluso con una unidad de tan solo 4 bytes, tenemos que 32 bits de
memoria requieren solamente 1 bit en el mapa. Si la unidad de asignación es de gran tamaño, habrá pocas unidades,
luego el mapa de bits ocupará poco espacio; pero por contra, tenemos que puede empezar a ser significativo el espacio
correspondiente al 50% de memoria que por término medio se desperdicia en la última de las unidades asignadas al
proceso. Este método es simple y sencillo de implementar, pero tiene la desventaja de que cuando se buscan Q
unidades consecutivas de memoria para asignar a algún proceso, se debe buscar en el mapa de bits hasta encontrar Q
bits consecutivos a 0.Aunque esta operación es lenta, los procesadores actuales suelen incluir instrucciones de
búsquedas de cadenas de bits, favoreciendo así la utilización de los mapas de bits
19. GESTION DE LOS BLOQUES DE MEMORIA
Otra forma de llevar la cuenta de la memoria utilizada
es manteniendo listas enlazadas de bloques libres y
bloques asignados. En este caso, la memoria se
representa como una lista de bloques en la que cada
entrada o elemento de la lista indica un hueco (bloque
libre), con su dirección, tamaño, y dirección del
siguiente elemento.
Este sistema de gestión en el que los bloques libres se
mantienen en una lista, se presta muy bien a la
implementación de estos algoritmos:
El primero que sirva (first fit)
El siguiente que sirva (next fit)
El que mejor se adapte (best fit)
El que peor se adapte (worst fit)
EL PRIMERO QUE SIRVA
El gestor de memoria recorre la lista hasta encontrar un
hueco que sea suficientemente grande para satisfacer la Pues bien, lo que genera esto, a la larga, es que cerca de
petición. Si el hueco elegido no es exactamente del tamaño la cabecera de la cola se empiecen a formar una serie de
solicitado, se divide en dos partes: una (del tamaño bloques no contiguos y de pequeño tamaño, inservibles
solicitado) se le asigna al proceso que lo solicitó, y la otra para ningún proceso. A partir de esta situación, cada vez
(con la memoria sobrante) se deja como un hueco de menor que se realiza una búsqueda, hay que recorrer esta serie
tamaño. de bloques pequeños de manera infructuosa, hasta llegar
a los bloques que todavía no han sido partidos, o no se
han fraccionado demasiado. Esta fragmentación externa
es inevitable, pero lo malo es la pérdida de tiempo que
supone el atravesar reiteradamente la lista de los
bloques pequeños agolpados al comienzo de la lista
20. GESTION DE LOS BLOQUES DE MEMORIA
Este problema se soluciona con EL SIGUIENTE QUE SIRVA
Con este algoritmo, la lista es circular, es decir, que el último
nodo apunta al primero y la cabecera no es siempre la
misma, sino que va rotando por la lista, es decir, que el
orden de la lista va rotando. Comienza por apuntar al primer
bloque (en orden creciente de dirección), pero tras realizar
la primera búsqueda de un bloque, la cabecera apunta
ahora al bloque siguiente al asignado y así sucesivamente.
Esto hace que los bloques fraccionados no se amontonen en
una parte de la lista sino que se distribuyan de manera
Uniforme. Así, cada vez que se comienza una búsqueda, no
habrá que atravesar todos los bloques pequeños, sino
solamente alguno, con la consecuente ganancia de tiempo
EL QUE MEJOR SE ADAPTE
Recorre la lista completa para seleccionar el hueco que mejor se
adapte, o sea, el menor de los que sirvan, para no fraccionar un
bloque grande que pudiera servir para alguna petición posterior.
Este algoritmo es más lento que los anteriores, pues tiene que
recorrer la lista entera por cada petición. Sorprendentemente
también desperdicia más memoria, pues tiende a ir rellenando la
memoria con huecos tan pequeños que resultan inservibles. En
cambio, EL PRIMERO QUE SIRVA y EL SIGUIENTE QUE SIRVA, al ir
fraccionando bloques, por termino medio generan bloques más
grandes que sí pueden ser aprovechables.
21. SISTEMA BUDDY
Como hemos visto, el problema de los algoritmos
basados en el orden de los bloques por su
tamaño es la lentitud en la fase de liberación de
un bloque. El sistema BUDDY (o de los
compañeros) es un algoritmo que aprovecha el
hecho de que los ordenadores utilizan números
binarios en las direcciones, para así facilitar la
tarea de la compactación de huecos adyacentes
cuando se libera un bloque.
El gestor de memoria mantiene una serie de listas de bloques libres, una para los de tamaño 1 byte, otra para 2, otra
para 4, 8, 16, etc. hasta llegar a Q, siendo Q el tamaño total del área de memoria libre inicial. Atendiendo al ejemplo de
la Figura ,vemos que si, por ejemplo, partimos de un área de memoria de usuario de 1 Mbyte, inicialmente toda ella
está libre, se tiene una lista con una única entrada correspondiente a un bloque de 1 Mbyte y el resto de las listas están
vacías. Cuando se recibe una petición de un proceso A de un bloque de 70 Kbytes, se busca un bloque cuyo tamaño sea
la potencia de 2 que más se aproxime por exceso a los 70 K, esto es, 128 K. Pero no hay ningún bloque de 128 K, ni de
256 ni de 512. Entonces se parte el bloque de 1M en dos bloques de 512 K (a los que se les llama COMPAÑEROS. Uno
de estos bloques se divide a su vez en dos nuevos bloques de 256 K y por último uno de estos se vuelve a partir en otros
dos bloques de 128 K. De estos dos últimos bloques, uno de ellos se asigna al proceso que realizó la petición de los 70 K.
A continuación, se recibe una petición B de 35 K, cuya potencia de 2 más cercana es la de 64 K, pero no hay ningún
bloque de 64 K disponible, así que partimos uno de los bloques de 128 K en dos de 64, y uno de ellos se le asigna a B.
Para servir la petición C de 80 K, es necesario partir uno de los bloques de 256 K en dos de 128, asignando uno de ellos.
Obsérvese que cada vez que se ha dividido un bloque, se generan dos nuevos bloques ADYACENTES de la mitad del
tamaño
22. SISTEMA BUDDY
Ahora tenemos que el proceso A libera el bloque
de 128 K (de los que sólo utilizaba70). Se acude a
la lista de bloques de 128 K para ver si alguno de
los bloques de128 K es el compañero del liberado.
En nuestro caso no lo es, por lo que simplemente
se mete el bloque liberado en esta lista de bloques
de 128 K.
Seguidamente vuelve a haber una petición D de 60
K, y se asigna un bloque de 64K que había
disponible. Ahora se libera el bloque del proceso B
, de 64 K (ocupaba35), pero como su compañero
no está libre, no hay ninguna compactación. A
continuación finaliza el proceso D, liberando un
bloque de 64 K (ocupaba 60).
En este caso, tenemos que el compañero de este bloque sí esta libre, por lo que se compactan formando un bloque de
128 K. Seguidamente se comprueba que este nuevo bloque de 128 K tiene su compañero libre, por lo que se vuelven a
compactar para formar un único bloque de 256 K. Cuando se libera el bloque del proceso C , de128 K (ocupadas 80) se
pueden realizar 3 compactaciones consecutivas dando lugar al bloque original de 1 Mb de memoria libre
VENTAJAS Y DESVENTAJAS
La ventaja de este algoritmo sobre otros que también ordenan conforme al tamaño,es que cuando se libera un bloque
de tamaño 2k bytes, el gestor de memoria solamente tiene que buscar en la lista de huecos de longitud 2k para ver si la
compactación es posible.
La desventaja es su ineficiencia en el aprovechamiento de la memoria, debido a los redondeos hasta una potencia de 2
en el tamaño de los bloques asignados, dando lugar, en consecuencia, a una fragmentación interna
23. En las distintas estrategias de gestión de memoria vistas
hasta ahora, todas tienen un objetivo común: mantener
muchos procesos en memoria simultáneamente para
permitir la multiprogramación. Y en cualquier caso,
siempre hemos visto que se requiere que un proceso
esté completamente en memoria principal antes de que
se pueda ejecutar. Sabemos que para ejecutar las
instrucciones de un programa, éstas, al igual que sus
operandos, tienen que estar en memoria principal. Esta
imposición parece necesaria y razonable, pero por
desgracia limita el tamaño de un programa al espacio
de memoria física (RAM).Sin embargo, el examen de los
programas reales nos muestra que en muchos casos no
es necesario que el programa entero permanezca
siempre en memoria. Por ejemplo:
• Los programas suelen tener código para manejar
condiciones de error inusuales. Ya que estos errores
raramente se producen en la práctica, este código casi
nunca se ejecuta.
• A veces, las tablas, listas o matrices se declaran con
más tamaño del que luego realmente necesitan. Una
matriz se puede declarar de 100 por 100 aunque
raramente ocupe más de 10 por 10; o un ensamblador
puede tener una tabla para albergar a 3000 símbolos,
aunque la media de los programas no tengan más de
200.
•Ciertos programas pueden ofrecer ciertas
características que nunca o raramente se utilizan. Por
ejemplo ciertas funcionalidades de programas como
editores de texto, hojas de cálculo, bases de datos, etc
24. La memoria virtual es una técnica que permite la
ejecución de procesos que pueden no estar
completamente en memoria principal. La principal
ventaja de este esquema es que los programas pueden
ser mayores que la memoria principal.
La habilidad de poder ejecutar un programa que sólo
esta parcialmente en memoria principal acarrea los
siguientes beneficios:
• El tamaño de un programa no está limitado por la
cantidad de memoria física disponible. Los usuarios
escriben programas contando con un espacio de
direcciones virtuales extremadamente grande.
• Debido a que cada programa puede necesitar para
ejecutarse menos memoria que la que ocupa su
tamaño total, se pueden cargar más programas en
memoria para ejecutarlos al mismo tiempo, con la
consiguiente mejora en el aprovechamiento de la
CPU.
• Ya que de cada programa solamente hay que cargar
la parte necesaria en un momento dado, a la hora
de cargar un proceso o expulsarlo a disco, se
necesitan menos operaciones de E/S debidas al
intercambio, por lo que se consigue una ejecución
global más rápida.
Se trata de tener cargada en el disco la imagen del
proceso en ejecución, y en memoria principal solamente
la parte necesaria para la ejecución de la sección del
programa que se está ejecutando en cada momento.
25. El ancho del bus de direcciones establece el rango de
direcciones que puede generar la CPU (DIRECCIONES
VIRTUALES). Por otra parte tenemos la memoria física,
es decir, los transistores que forman bytes o palabras,
tal que cada uno de los bytes tiene una
dirección (DIRECCIONE REAL).
Normalmente pensamos que a cada dirección que sale
del procesador le corresponde una dirección física, pero
claro, puede suceder que haya instalada menos
memoria de la permitida por el bus de direcciones. Así,
el procesador va alimentándose de instrucciones y
datos que están en RAM, hasta que en un momento
dado hace referencia a una dirección de memoria que
realmente no existe, es decir, que no tiene su
correspondiente memoria física. Sin embargo, la
instrucción o dato que se está referenciando sí se
encuentra en la imagen del proceso que reside en el
disco duro, por lo tanto, lo que hay que hacer es traerlo
a memoria principal para permitir que la CPU termine
de obtener el dato solicitado a la memoria.
Para implementar los sistemas de memoria virtual,
normalmente se utiliza la PAGINACION, También se
puede implementar mediante la SEGMENTACION, como
lo hace el sistema OS/2 de IBM, pero los algoritmos son
bastante más complejos que los de la paginación, pues
a diferencia de las páginas, los segmentos son de
tamaño variable
26. PAGINACION
Como hemos dicho con Memoria Virtual no se trae todo el proceso a memoria principal, sino que cuando se hace
referencia a una dirección de memoria virtual cuya correspondiente memoria física reside en disco, se trae el
contenido de disco a RAM
Si el tamaño de los bloques de intercambio es variable,
por ejemplo de 1, 2, 3 y 4Kb, podría fácilmente darse el
caso de que sea necesario traer de disco a memoria un
bloque de 3 Kb y en memoria tener dos huecos libres de
1 Kb y 2 Kb, pero no contiguos, con lo que a primera vista
no podríamos traer el bloque de 3 Kb a pesar de
disponer de 3 Kb libres en memoria principal, debido a la
fragmentación externa existente. Para eliminar el
problema de la fragmentación externa, lo que se hace es
utilizar bloques de intercambio de tamaño único y dividir
la memoria principal en trozos del mismo tamaño (de
512 bytes a 8 Kb), con lo cual a la hora de traer cualquier
bloque de disco a memoria, si hay un hueco libre, seguro
que cabe, y además no se desperdicia ningún espacio
sobrante. A cada uno de los bloques de intercambio que
están en el disco o en memoria principal le llamaremos
PAGINA y a cada uno de los huecos en que está dividida
O sea, que los marcos son espacios de memoria principal de la memoria principal (esté libre u ocupado) le
tamaño fijo y de direcciones consecutivas, que pueden denominaremos MARCO
albergar bloques de información, exactamente del mismo
tamaño, denominados páginas.
Nos encontramos, entonces, con que el espacio de direccionamiento virtual está dividido en páginas y el espacio de
direccionamiento físico está dividido en marcos. Así pues, cuando la CPU genera una dirección virtual, ésta estará
contenida en una de las páginas. Si tal página se encuentra en alguno de los marcos de memoria física, se accede al dato;
si no, habrá que cargar esa página, residente en el disco duro, en alguno de los marcos de memoria principal, y a
continuación acceder al dato
27. PAGINACION
En un sistema sin memoria virtual, la dirección virtual 1
se corresponde con la dirección física 1, la 2 con la 2, etc.
¿ Sigue siendo esto válido con la memoria virtual? Pues
no. Si fuese así, en un sistema con una espacio de
direccionamiento virtual de 24 Mb y una memoria física
de 4 Mb, por ejemplo, esto querría decir que una
referencia a una dirección comprendida en el rango de
los 4 primeros megabytes de memoria, siempre
encontraría la página correspondiente en memoria
principal, pero ¿qué pasaría al hacer referencia a una
dirección superior? habría que traerla a memoria
principal, pero ¿a dónde? si no existen las direcciones
físicas correspondientes. Esto nos lleva a que hay que
desligar la correspondencia directa y estática entre
direcciones físicas y virtuales, o lo que es lo mismo, hay
que desligar una relación estática entre páginas y
marcos, y hacer que su relación se establezca dinámica e
indirectamente mediante una tabla, a la que llamaremos
1. La CPU hace referencia a una dirección virtual de Memoria. TABLA DE PAGINAS . Esta tabla, además de guardar la
2. Se calcula a qué página corresponde tal dirección. correspondencia entre páginas y marcos, mantiene más
3. Mediante la tabla de páginas se comprueba si la página se información de control, como por ejemplo, el
encuentra en algún marco de memoria física o en disco. denominado BIT DE PRESENCIA , que indica si una
4. Si se encuentra en un marco, se traduce la dirección virtual a la
dirección física que ocupa la página.
página dada se encuentra en un marco de memoria o no
5. Si no se encuentra en un marco, hay que traer la página desde disco
a un marco libre de memoria. Posteriormente habrá que traducir la
dirección virtual a la dirección física, dependiendo del marco asignado
a la página.
6. Por último, se realiza la operación de L/E sobre memoria principal
solicitada por la CPU.
28. PAGINACION
En este ejemplo la dirección lógica 0020FF se descompone en dos campos:
002 que es el número de página y 0FF que es el desplazamiento relativo dentro de la página.
De la tabla de páginas se comprueba que la página 002 le corresponde el marco de página
104, luego la dirección física se construye sustituyendo, en el primer campo en el que se divide la
dirección, el número de página por el marco de página correspondiente, resultando así la dirección
física 1040FF.
29. IMPLEMENTACION DE LAS TABLAS DE PAGINAS
El objetivo de las tablas de páginas es asociar a las páginas un marco de página. Esto se puede ver como una función
matemática, que tiene como argumento el número de página y que da como valor el número del marco de página
correspondiente.
Ejemplo. Práctica de paginación
Supóngase que la tabla de páginas de un proceso que se está ejecutando en el procesador es:
Donde:
• R: es el bit de referencia. R=1 (la página ha sido referenciada).
• M: es el bit de modificación. M=1 (la página ha sido modificada).
• V: es el bit de presente/ausente. V=1 (la página está en memoria principal, tiene un marco asociado)
El tamaño de las páginas es de 1 Kb.
El marco 0 está cargado en la dirección física 0 y el resto sucesivamente.
Se pide ¿A qué direcciones físicas corresponden las siguientes direcciones virtuales?:
1. (1, 125)
2. (2, 324)
3. (5, 322)
4. (7, 321)
5. (3, 1026)
El formato en el que se da la dirección virtual corresponde a:
(nº de página, desplazamiento)
30. IMPLEMENTACION DE LAS TABLAS DE PAGINAS
Solución:
La dirección física, de acuerdo con el sistema de paginación, será el número de marco multiplicado por el tamaño de
marco más el desplazamiento dentro de él:
Dirección física= Nº de marco x Tamaño de marco + Desplazamiento
Se ha de tener en cuenta que:
- Exista la página
- El desplazamiento no supere el tamaño del marco
Si no, en ambos casos se tendrá un error de direccionamiento. Además la página ha de tener un marco asociado, si no
tendrá lugar un fallo de la página.
1. Dirección virtual: (1, 125) . Dirección física = 7 * 1024 + 125 = 7293
2. Dirección virtual: (2, 324). La página 2 no tiene ningún marco asociado, por lo tanto, tiene lugar un fallo de página.
3. Dirección virtual: (5, 322) . Dirección física = 0 * 1024 + 322 = 322
4. Dirección virtual: (7, 321). Error de direccionamiento porque no existe la página 7.
5. Dirección virtual: (3, 1026). Error de direccionamiento porque 1026 es mayor que 1024 por lo que el desplazamiento
es mayor que el tamaño de marco.
31. Cuando en la referencia a una dirección virtual, la página
correspondiente no se encuentra en memoria principal,
entonces decimos que se produce una FALLA DE PAGINA
En este caso, las acciones a realizar son, son las siguientes:
1. La CPU genera una dirección virtual.
2. La MMU consulta el bit de presencia de la tabla de
páginas y se comprueba que la página de la dirección
referenciada no se encuentra en memoria principal.
3. La MMU pone la CPU en espera y genera una
interrupción (que suele ser (ERROR DE BUS) que
es capturada por el sistema operativo.
4. Las rutinas de carga de páginas del sistema operativo se
encargan de localizar la página referenciada en el disco y
cargarla en un marco libre de la memoria principal.
5. Se actualiza la tabla de páginas indicando que está
presente y el marco en el que ha sido cargada.
6. Termina el tratamiento de la excepción.
7. La MMU indica a la CPU que debe rearrancar la
instrucción que provocó la falta de página.
8. Se continua la ejecución de la instrucción a partir del
direccionamiento que produjo la falta de página.
Uno de los sistemas más comunes de memoria virtual es el denominado demanda de la página, que es similar a un
sistema de paginación con intercambio.
La tarea adicional del hardware de traducción de direcciones en sistemas virtuales es detectar si el elemento
Referenciado está en memoria real o no. Para ello se añade un bit de presencia a cada entrada de la tabla de páginas (en
el caso de gestión paginada).
32. ESTRUCTURA DE LA PAGINA DE ENTRADA
Dijimos que la función de correspondencia entre las direcciones virtuales y las físicas o reales se realiza mediante una
tabla de páginas. Esta tabla tiene tantas entradas como páginas existentes y cada entrada contiene información que,
aunque varía de unos sistemas a otros, suele estar compuesta de los siguientes campos:
PROTECCION
Expresa los permisos de acceso del proceso. En caso de tener permiso para la operación de lectura/escritura que se
va a realizar, se consulta el resto de los campos.
BIT DE PRESENCIA
Indica si la página está presente en memoria principal o no. Sise encuentra en RAM, también tienen sentido los
siguientes campos.
MARCO OCUPADO
Si la página se encuentra en memoria principal, este campo expresa el marco que la contiene.
MODIFICADA (bit de ensuciado)
Este campo indica si el contenido de la página ha sido modificado desde que se trajo de la memoria secundaria.
REFERENCIADA
Este campo booleano se pone a cierto cada vez que se hace referencia a cualquier dirección de la página. Lo utiliza el
sistema operativo para ayudar a los algoritmos de sustitución de páginas. En caso de que la página referenciada no
esté cargada en memoria, sabemos que se encuentra en el área de intercambio del disco, pero ¿ en qué lugar
concreto? La dirección concreta de cada página virtual en el disco (cilindro, pista, sector), no se indica explícitamente
en ningún campo de la tabla de páginas. En su lugar, lo que se hace es calcular la dirección. Esto se puede realizar
fácilmente ya que el área de intercambio ocupa una porción contigua del disco, o sea, que no está fragmentada, bien
sea por estar en una partición reservada para ello (caso de Unix y Linux), o por estar en un fichero creado en por el
sistema operativo en la generación del sistema(caso de Windows: Pagefile.sys) y que tampoco está fragmentado.
Conociendo la dirección de comienzo del área de intercambio y el tamaño de la página, el cálculo de la dirección de
cada página es inmediato. De esta forma se ahorra mucho espacio en la tabla de páginas
33. GESTION DE LA MEMORIA
Una gestión de la memoria basada en el mecanismo de demanda de página tiene un considerable efecto
sobre el rendimiento del computador. Esto se puede ver calculando el tiempo promedio de acceso (tpa):
tpa = ( 1 - p ) * am + p * fp
Donde:
• am = tiempo medio de acceso a memoria
• p = probabilidad de que ocurra un fallo
• fp = tiempo que lleva resolver un fallo de página
Muchos procesos no usan todas sus páginas, por tanto no hay que traerlas todas, pudiéndose ejecutar
más procesos. Por ello, se deben tener políticas de la gestión de la memoria virtual como las siguientes:
• Política de asignación: qué cantidad de memoria real se asigna a un proceso
• Política de acceso: qué elementos se incorporan y cuando
• Política de sustitución: si no existe memoria libre cuando se debe añadir un nuevo elemento, que
elemento se desaloja.
• Política de ubicación: dónde se coloca el nuevo elemento.
34. Pensemos que la memoria puede estar
completamente ocupada por dos motivos:
1. Se utilizan muchas páginas del proceso en
ejecución.
2. Se ha ido incrementando el grado de
multiprogramación hasta ocupar toda la
memoria principal, por lo que ésta se
encuentra ocupada por páginas de diversos
procesos.
En este último caso, se podría optar por sacar
alguno de los procesos completamente a disco,
es decir, todas sus páginas; pero puede que no
sea una buena idea, porque a lo mejor en la
siguiente porción de tiempo hay que volver a
cargar todas esas páginas. Puesto que lo único
que se necesita en este momento es espacio
para una página, la mejor salida para cuando se
produce una falta de página suele ser la
sustitución de alguna de las páginas que
actualmente residen en memoria principal por
la que acaba de ser referenciada. El proceso de
sustitución tiene los siguientes pasos
35. 1. Se elige la “víctima” , es decir, una página
cargada en memoria principal, para llevarla a
disco y dejar un marco libre. La elección se
realizará dependiendo de la política de
sustitución de páginas.
2. Se lleva la página elegida al área de
intercambio del disco.
3. En la entrada de la tabla de páginas
correspondiente a la víctima, se indica que
“no está presente”.
4. Se trae la página referenciada al marco que
se acaba de dejar libre.
5. Se actualiza la entrada de la tabla de páginas
correspondiente a la página que se acaba de
traer a memoria principal, indicando que
está presente y actualizando tanto el número
de marco que ocupa como los campos de
control que sean necesarios en cada caso.
Nos queda un problema por resolver:¿ cómo
elegir la página a sustituir cuando no hay
ningún marco libre? Veamos los distintas
políticas de sustitución de páginas
36. POLÍTICAS DE SUSTITUCIÓN DE PÁGINAS
Al producirse un fallo de página, el S.O. debe de elegir una página de
las que están en memoria, para sustituirla por la que se solicita. La
página que entra en memoria se escribe directamente encima de la
que se retira.
Es posible elegir aleatoriamente una página para sustituirla por la
solicitada, pero el rendimiento es mayor si se elige una página que no
se usa. Se han estudiado muchos algoritmos para la sustitución de
páginas. todos persiguen lo mismo: seleccionar páginas que causen la
menor tasa posible de faltas de página.
A continuación se describen algunos de los algoritmos más
interesantes.
37. Algoritmo de sustitución FIFO: Primero en entrar, primero en salir
Se sustituye la página que lleva más tiempo en memoria. En realidad, no es necesario guardar el tiempo de
entrada, ya que se puede crear una cola según el orden de entrada, con todas las páginas de la memoria,
cuando hay que sustituir una página se elige la primera de la cola y la que se trae se inserta al final de la
cola.
Por ejemplo, se considera la siguiente secuencia de acceso a páginas (8, 1, 2, 3, 1, 4, 1, 5, 3, 4, 1, 4). Si
inicialmente la memoria está vacía, ¿cuantos fallos de página habrá?. Con tres marcos de página.
Con el algoritmo FIFO s e
producen 10 fallos de página
38. La política FIFO es fácil de entender y de implementar, sin embargo su comportamiento no siempre es bueno.
Resulta buena elección cuando se descargan páginas correspondientes a módulos que no se van a volver a utilizar,
por ejemplo, la parte de inicialización de un programa; pero resulta pésimo cuando se elige una página que contiene
ciertas constantes que se cargaron al principio y que se utilizan a lo largo de todo el programa, por lo que enseguida
va a ser referenciada.
Parece obvio, a mayor número de marcos de memoria, menos faltas de página. Sin embargo hay situaciones (como
la descrita en la Figura) en las que utilizando el algoritmo FIFO, al incrementar el número de marcos, aumentan las
faltas de página. Cuando se produce esto, tenemos lo que se conoce como ANOMALIA DE BELADY
39. ALGORITMO DE SUSTITUCIÓN OPTIMA
Se sustituye la página que tardará más en volverse a utilizar. Este algoritmo es irrealizable ya que el sistema
operativo no sabe a priori qué página se referenciarán en el futuro.
Se usa como nivel de medida para los demás algoritmos de sustitución de páginas, comparándolo mediante
simulación o en una segunda ejecución.
Por ejemplo, considerar la cadena de referencia del ejemplo anterior FIFO y compararla con el
algoritmo óptimo.
Cadena de referencia (8, 1, 2, 3, 1, 4, 1, 5, 3, 4, 1, 4).
Se observa que frente a los diez
fallos de página del algoritmo FIFO,
el algoritmo óptimo presenta siete
fallos de página.
Lo que supone que el algoritmo FIFO
es un 43% peor que el óptimo.
40. Algoritmo de sustitución LRU: La página menos usada últimamente
Es una mejora respecto al FIFO pero necesita un considerable apoyo de recursos de la máquina. Se sustituye la página que
no ha sido utilizada durante un período mayor de tiempo. Es equivalente a aplicar el algoritmo óptimo hacia atrás en el
tiempo.
Puesto que no es posible saber cuándo va a ser referenciada una página, quizás se podría utilizar el pasado más reciente
como una aproximación al futuro más cercano, es decir, elegir como víctima a LA PAGINA QUE LLEVA MAS TIEMPO SIN
SER REFERENCIADA
La sustitución mediante LRU asocia a cada página la hora a la que fue referenciada por última vez. Cuando hay que
reemplazar una página, simplemente se elige la que tiene la hora de referencia más antigua
(8, 1, 2, 3, 1, 4, 1, 5, 3, 4, 1, 4)
Se producen nueve fallos de página.
Aunque presenta más fallos que en el algoritmo
óptimo, es una mejora con respecto al algoritmo
FIFO.
41. Algoritmo de sustitución de la Segunda Oportunidad
Este algoritmo es una modificación del algoritmo de
sustitución FIFO. La modificación consiste básicamente
en examinar el bit de referencia de las páginas que se
seleccionan para sustituir. Si el bit de referencia es 0, la
página no se ha utilizado además de ser antigua. Si el bit
de referencia es 1, entonces se pone a cero y la página
se coloca al final de la cola.
Este bit es un campo más de la tabla de páginas, y se
pone a 0 al cargar la página. Cuando una página es
referenciada por la CPU (lectura o escritura), el
hardware pone el bit de referencia a 1. Así, pasado un
tiempo, examinando el bit de referencia se puede
determinar qué páginas han sido accedidas y cuáles no.
De esta forma, ya tenemos una primera aproximación al
LRU.
Este algoritmo sigue los siguientes pasos:
1. La lista FIFO circular se empieza a formar a medida
que se empiezan a cargar las primeras páginas en
memoria. Nos serviremos de un puntero que en
principio apunta a la primera página.
2. En un momento dado, al ir a cargar una nueva página,
se detecta que ya no hay ningún marco libre. Hay que
elegir una página a sustituir entre las de la lista.
3. Se analiza la página indicada por el puntero.
4. Si su bit de referencia está a 0, es la elegida. Se saca
de la lista y en su lugar se inserta la nueva página que se
trae de disco. Se avanza el puntero al siguiente elemento
de la lista. Finaliza la selección.
5. Si está a 1, se pone a 0 y se avanza el puntero al
siguiente elemento de la lista. Vuelta al paso 3.