Вы находитесь на странице: 1из 13

Capítulo 3: Estructura del sistema operativo

Gestión de memoria principal


Podemos entender la memoria principal como un inmenso casillero, donde cada casilla está
numerada (con una dirección) y puede almacenar una palabra.

Una palabra es el conjunto de bits que la arquitectura de un ordenador puede manejar como un
todo. Los tamaños de palabra más comunes son de 16, 32 ó 64 bits.

Como dijimos al hablar de procesos, para que un programa pueda ejecutarse, sus instrucciones y
sus datos tendrán que estar presentes en la memoria principal del sistema, lo que conocemos como
memoria RAM. Como hemos visto más arriba, planificando el uso del procesador, mejoraremos
el rendimiento general del sistema, pero esto implicará la necesidad de compartir la memoria
principal entre varios procesos de forma simultánea. Por lo tanto, una buena administración de la
memoria, repercutirá de forma inmediata en el comportamiento de todo el sistema informático.

El gestor de memoria deberá asignar la porción necesaria de memoria principal a cada proceso
que lo necesite.

Como ya dijimos, para ejecutar una instrucción, habría que leerla desde la memoria a un registro
del procesador y decodificarla (averiguar qué significa). A continuación, es posible que el sistema
deba volver a la memoria para obtener los datos implicados en la operación. Finalmente, es común
que los resultados obtenidos también haya que guardarlos en la memoria. Por todo ello, si la
gestión de la memoria no es adecuada, el rendimiento general del sistema se verá inmediatamente
disminuido.

Los sistemas operativos modernos son, casi siempre, multiprogramados. Es decir, ejecutan varios
procesos de forma concurrente. Esto significa que la memoria debe dividirse para darles cabida.

Cuanto más eficaz sea ese reparto, más procesos podrán ejecutarse a la vez, lo que redundará en
un mayor rendimiento (no debemos olvidar que el procesador es el elemento más rápido del
sistema y que el objetivo principal consiste en que siempre tenga instrucciones listas para ser
ejecutadas).

Además, la gestión de memoria deberá cumplir con las siguientes necesidades:

 Protección: Debe evitarse que un proceso haga referencia a posiciones de memoria de un


proceso diferente.

Como el sistema operativo no puede anticiparse a todos los accesos que realizará un
proceso durante su ejecución, es el procesador quien debe tener un mecanismo que
intercepte los accesos no permitidos.

 Reubicación: Como veremos más adelante, una forma de dar cabida a más procesos
consiste en descargar a disco la totalidad o una parte de la memoria ocupada por un
proceso (por ejemplo, mientras se encuentra bloqueado). La reubicación consiste en que
al volver a la memoria, no sea necesario que ocupe la posición original.

Para que esto sea posible las referencias que hagan los programas a direcciones de
memoria deben ser lógicas y serán el hardware y el sistema operativo quienes colaboren
para realizar la traducción.
 Compartición: Debe existir la posibilidad de que varios procesos compartan información
a través de una zona de memoria compartida. Por ejemplo, es más eficiente que varios
procesos accedan al mismo código de una biblioteca de funciones compartida (lo que en
Windows conocemos como DLL), que mantener varias copias de ésta.

Igual puede ocurrir con datos que manejen diferentes procesos.

Sin embargo, para tratar de comprender la complejidad de la gestión de memoria, estudiaremos


distintos mecanismos, de más sencillo a más complejo, pudiendo entender la explicación como
una evolución.

Gestión de memoria para un solo proceso

En los primeros ordenadores, la memoria principal se dividía en dos partes: una para la parte del
sistema operativo que debía estar siempre en memoria (que recibía el nombre de monitor) y otra
para un único proceso de usuario. Un ejemplo de este esquema lo representan las primeras
versiones del sistema operativo MS-DOS, aunque, en su caso, la parte superior del espacio de
direcciones lo ocupaba el software de la BIOS.

Lógicamente, sólo podía ejecutarse un proceso cada vez y, cuando éste terminaba, el control
volvía al sistema operativo.

Gestión de memoria con particiones fijas

El administrador (o incluso un operador) divide la memoria en fragmentos antes del inicio de la


ejecución de los programas. Cuando llega un nuevo proceso, el planificador lo ubica en la
partición con el tamaño más adecuado y, cuando un proceso acaba, su partición queda libre para
un nuevo uso.

Este método recibe también el nombre de Multiprogramación con un número fijo de tareas, o
MFT (del inglés, Multitasking with a Fixed number of Tasks).

Era frecuente organizar la carga de trabajo según las particiones de memoria, creando una lista
para cada una de ellas en función de su tamaño.
Otra opción es disponer de una cola única y elegir las tareas que se adapten a las particiones que
quedan libres, aunque esto discrimina a las tareas con menos requisitos, porque tienden a
desperdiciar espacio.

En este tipo de esquema surge el concepto de intercambio (en inglés, swapping), que consiste,
básicamente en mover a memoria secundaria (normalmente disco), los procesos que se encuentran
bloqueados en espera de un suceso. De esta forma, se puede dar cabida a nuevos procesos.

El módulo del sistema operativo que se encarga del intercambio se llama, precisamente,
intercambiador y se encarga de elegir los procesos que deben moverse de memoria principal a
secundaria y a la inversa. También administra el espacio reservado en la memoria secundaria
para estos fines.

Relacionado con el concepto de intercambio se encuentra el concepto de reubicación que consiste,


básicamente, en que un determinado proceso puede ejecutarse en diferentes particiones de
memoria, no sólo en sucesivas ejecuciones, sino incluso durante la misma ejecución. De esta
forma, cuando un programa haga referencia a una posición de memoria x, en realidad estará
refiriéndose a una posición relativa al comienzo de la partición. Es decir, si la partición comienza
en la dirección y, la posición exacta a la que hace referencia el programa sería la y + x.

Por consiguiente, podremos hablar de dos tipos de direcciones de memoria:

 La dirección lógica o virtual, que es la dirección a la que hace referencia el programa (en
la explicación anterior, la dirección x)
 La dirección física, que es la dirección real a la que se accede (en la explicación anterior,
la dirección y + x).

Para realizar estos cálculos a gran velocidad, el procesador dispondrá de un registro base que
contiene la dirección de inicio de la partición que ocupa el proceso.
Además, es necesario establecer un mecanismo de protección para evitar que un proceso, por
error o de forma intencionada, pueda acceder a posiciones de memoria que se encuentren más allá
del espacio que tenga asignado. Para lograrlo, se implementa un registro límite que contiene la
longitud de la partición que se está utilizando. De esta forma, las referencias de cada proceso se
calculan añadiendo al registro base la dirección lógica y comprobando que no se excede el
registro límite.

La dificultad en este esquema de memoria es obtener el equilibrio entre el tamaño de las


particiones y el de los procesos que deben ubicarse en ellas. En este sentido, podemos
encontrarnos con dos formas diferentes de desaprovechamiento:

 Fragmentación interna: Ocurre cuando existen particiones grandes y la mayoría de los


procesos no las ocuparán enteras. Es decir, el derroche de memoria se produce dentro de
la asignación al proceso.
 Fragmentación externa: Ocurre cuando hay particiones excesivamente pequeñas y es
difícil encontrar procesos que quepan dentro. Es decir, el derroche se produce entre la
memoria que no está asignada a ningún proceso.

Un ejemplo de este esquema lo representan los grandes ordenadores de IBM que incorporaban el
sistema operativo OS/360.

La Gestión de memoria con particiones fijas tiene las ventajas de una implementación sencilla y
que necesita poco software por parte del sistema operativo. Además, ofrece poca sobrecarga de
procesamiento.

Sin embargo, el número de procesos que pueden estar activos es limitado. El resto deben
encontrase Suspendidos (descargados a memoria secundaria). Además, los procesos más
pequeños tienden a desperdiciar gran parte del tamaño de las particiones.
Gestión de memoria con particiones variables

Se basa en la idea de que las particiones de memoria vayan cambiando de tamaño a lo largo del
tiempo. El sistema dispondrá de una tabla donde se representen las zonas de memoria que se
encuentran ocupadas.

Cuando llega un proceso, se busca un bloque de memoria suficientemente grande para contenerlo
y se le asigna sólo la porción necesaria. El resto del bloque queda libre para otra asignación.
Cuando un proceso termina, la memoria que ocupaba queda disponible y, si se encuentra junto a
otro bloque libre, se une a él.

En cualquier caso, el problema de esta idea es que, si hay múltiples asignaciones y liberaciones
de memoria, ésta tenderá a tener una gran fragmentación externa.

Sin embargo, el problema se resuelve aplicando, cuando sea necesario, un procedimiento de


compactación de memoria, que consiste en desplazar los bloques asignados hacia un extremo y
uniendo a su vez todos los bloques libres en uno más grande. Lógicamente, el tiempo que debe
emplearse para realizar esta tarea es su gran inconveniente.

En cuanto a la asignación, debe estar destinada a retrasar el momento en que sea necesaria la
compactación de memoria. Existen diferentes opciones a la hora de elegir el bloque más adecuado
para un proceso:

 El primer ajuste: Se asigna el proceso al primer bloque libre en el que quepa. Con el
tiempo, los espacios disponibles del principio de la memoria tenderán a ser pequeños y
difíciles de utilizar.

Es bastante rápido, aunque necesita recorrer los primeros bloques hasta encontrar un
hueco disponible.

 El siguiente ajuste: Busca un hueco a partir de la última asignación. Es frecuente que se


reserve espacio en el bloque que queda disponible al final de la memoria, que se divide
en pequeñas porciones.
Lo normal es que requiera frecuentes compactaciones de memoria para obtener un bloque
grande al final de la memoria.

 El mejor ajuste: Se asigna el bloque más pequeño cuya capacidad sea suficiente para
contener al proceso. El resultado es peor que el anterior, porque los fragmentos resultantes
son más pequeños y, por lo tanto, difíciles de utilizar.

Lo normal es que requiera frecuentes compactaciones de memoria.

 El peor ajuste: Se asignan primero los fragmentos de memoria más grandes, esperando
que los restos obtenidos puedan ser más sencillos de utilizar. Suele requerir
compactaciones de memoria más tardías, aunque necesita recorrer todos los bloques
libres antes de elegir cada destino.

Usando la particiones variables, se pueden seguir aplicando técnicas de intercambio, para mover
a memoria secundaria, los procesos que se encuentran bloqueados en espera de un suceso.

Reubicación

Según lo que acabamos de decir, cada vez que un proceso (o una parte de este), se descargue a
memoria secundaria (o cada vez que se compacte la memoria), al volver a ejecutarse, podrán
haber cambiado las direcciones físicas donde se encontraba. Este mecanismo recibe el nombre de
Reubicación.

Para lograr que la Reubicación funcione, el proceso de traducción de direcciones virtuales a


direcciones físicas necesitará de la participación de dos registros en el procesador:

 Registro base: Que contiene la dirección en memoria principal del proceso.


 Registro límite: Que contiene la última posición a la que puede hacer referencia el
proceso.

Si cuando sumamos la dirección relativa al registro base obtenemos un resultado inferior a dicho
registro base o superior al registro límite, el procesador genera una interrupción que deberá ser
procesada por el sistema operativo.
Paginación

En la Gestión de memoria con particiones variables, hemos visto que la memoria disponible
tiende a no estar en posiciones contiguas, por lo que se produce una considerable fragmentación
externa (la memoria disponible se dispersa a lo largo del espacio de direcciones.

Para resolver esta circunstancia, se planteó la posibilidad de que el espacio de memoria que usa
un determinado proceso no tuviese que estar en posiciones contiguas. Así, en un esquema de
paginación, la memoria se divide en trozos del mismo tamaño que reciben el nombre de marcos
de página (o, en inglés, frames). Del mismo modo, los procesos se dividen en fragmentos del
mismo tamaño denominados páginas.

A modo de ejemplo, imagina que el proceso que queremos cargar en memoria lo representáramos
con el zumo contenido en la botella de la siguiente imagen. Por su parte, la memoria donde
pretendemos almacenarlo estaría representada por los vasos. Cada vaso, sería el equivalente a un
marco de página de la memoria.

Así, cuando carguemos el proceso en memoria, cada página (cantidad de zumo del tamaño de un
vaso) se ubicará en un marco de página diferente.

De este modo, cuando llegue un nuevo proceso, el único problema será encontrar la cantidad
suficiente de marcos de página disponibles en la memoria principal.

Gracias a este planteamiento, se acaba con la fragmentación externa, y la fragmentación interna


quedará reducida al último marco de pagina asignado a cada proceso. Lo que equivaldría, en
nuestro ejemplo, al último vaso.

Cabe esperar que, por término medio, la mitad del último marco de página quede desocupado.

Como podrás suponer, este esquema necesita un método que traduzca las direcciones virtuales a
direcciones físicas, teniendo en cuenta la ubicación real de cada marco de página. Este método
se basa en la creación de una tabla de páginas, para cada proceso, en el momento de cargarlo en
memoria. En ella se establecerá el paralelismo entre cada página y su marco de página
correspondiente.

Por lo tanto, las direcciones virtuales constarán de un número de página y un desplazamiento


dentro de ella. El número de página actuará como índice en la tabla de páginas.

Además, es frecuente que el sistema operativo mantenga una lista de marcos de página
disponibles.

Las direcciones virtuales también suelen llamarse direcciones relativas y suelen asignarse
durante la compilación del programa, siendo relativas al comienzo del mismo (que será la
dirección 0 de la página 0).

Dado que este método implica constantes traducciones de direcciones virtuales a direcciones
físicas, para evitar que el sistema sufra una importante penalización de rendimiento, habrá que
recurrir a un hardware específico para la traducción.

Por otra parte, el sistema operativo dispondrá de un mapa de la memoria, con una entrada por
cada marco de página, donde se indique cuáles están libres y cuáles ocupados.

Otro aspecto a tener en cuenta será el tamaño de los marcos de página:

 Con marcos de página pequeños, tendremos poca fragmentación interna y tablas de


páginas grandes.
 Con marcos de página grandes, tendremos más fragmentación interna y tablas de
páginas pequeñas.

Usando la paginación, se pueden seguir aplicando técnicas de intercambio, para mover a memoria
secundaria, los procesos que se encuentran bloqueados en espera de un suceso.

Otras consideraciones a tener en cuenta

Cuando se utilizan tablas de páginas de gran tamaño, puede que buena parte de las tablas de los
procesos activos se encuentren descargados en memoria secundaria, perjudicando su
rendimiento.
Además, el diseño de la memoria secundaria suele estar orientado al manejo de bloques grandes,
que se adecuan mejor al uso de páginas de mayor tamaño.

Sin embargo, si utilizamos páginas pequeñas, cuando un proceso lleve un tiempo ejecutándose,
todas sus páginas de memoria tendrán referencias con una alta probabilidad de ser utilizadas, lo
que reducirá la tasa de fallos de página (ver el principio de cercanía, más adelante, en el apartado
Gestión de Entrada/Salida).

En cualquier caso, el tamaño de página deberá estar relacionado con la cantidad de memoria
principal y el uso que vayamos a hacer de ella. Por ejemplo, las técnicas de programación
orientada a objetos produce programas con bloques de código pequeños que generan referencias
a posiciones de memoria dispersas en breves periodos de tiempo. Por su parte, las aplicaciones
multihilo suelen modificar bruscamente el flujo de referencias a instrucciones y datos.

Segmentación

Hasta ahora, hemos visto el problema de la gestión de memoria desde el punto de vista del sistema
operativo, donde la asignación de memoria se realiza en función del tamaño total de un proceso
o dividiendo éste en porciones de la misma longitud. Sin embargo, los programadores y los
usuarios necesitan manejar sus datos de un modo más flexible: Tanto las funciones y
procedimientos en las que se dividen los programas, como las estructuras en las que se organizan
los datos (como tablas o pilas), tienen tamaños diversos.

Por lo tanto, podemos decir que un programa es un conjunto de elementos lógicos de tamaños
variables.

Es como si el zumo del ejemplo anterior fuese en realidad una mezcla (por ejemplo, naranja, kiwi
y fresa) y nos interesara mantenerlos separados. Entonces, podríamos utilizar varios envases más
pequeños. Todos juntos representarían al mismo proceso:

Para dar cobertura a este planteamiento, la segmentación plantea que en el momento de compilar
un programa, éste se convierta en un conjunto de segmentos a los que se asignará un identificador,
un punto de inicio y un tamaño. Las direcciones se expresarán mediante un número de segmento
y un desplazamiento dentro de él, y el tamaño asegura que no se realizan referencias a direcciones
ilegales dentro del segmento.

Además, como cada componente lógico del proceso se encuentra en un segmento diferente,
podemos mejorar la protección haciendo que los segmentos que contienen código sean de solo
lectura. También podemos asegurar que un fragmento de código no acceda al código de un
fragmento diferente.

Por otra parte, al encontrarse cada bloque lógico del programa en un segmento distinto, podría
compartirse código entre diferentes programas de un modo sencillo.
De forma parecida a como ocurría en la paginación, la conversión de una dirección virtual a su
correspondiente dirección física se realiza utilizando una tabla de segmentos.

Como en el caso de la paginación, la segmentación puede extender el espacio físico de


almacenamiento utilizando técnicas de intercambio.

Paginación y segmentación combinadas

Dado que la segmentación ofrece ventajas desde el punto de vista del usuario, pero la paginación
simplifica la perspectiva del sistema operativo, cuando el tamaño de los segmentos es grande, es
frecuente que se utilice una combinación de ambas. La idea es dividir cada segmento en páginas
de longitud fija para su ubicación en memoria.

Siguiendo con nuestro ejemplo anterior, el contenido de cada botella utilizaría su propio conjunto
de vasos para cargarse en memoria. Aunque no debemos olvidar que todo el zumo forma parte
del mismo proceso:

Como en la segmentación pura, las direcciones se expresarán mediante un número de segmento


y un desplazamiento dentro de él. Sin embargo, ahora en la entrada de la tabla de segmentos la
dirección base hace referencia al inicio de la tabla de páginas asociada al segmento. El
desplazamiento dentro del segmento se divide por el tamaño de página para encontrar el marco
de página adecuado. El resto de la división anterior representará la dirección que estamos
buscando.
Este método ofrece una gran flexibilidad, pero tiene tres inconvenientes:

 El proceso de traducción es más complejo, por lo que consumirá más recursos.


 Dado que cada segmento tiene su propia tabla de páginas, el espacio destinado al
almacenamiento de tablas de páginas será mucho mayor.
 Como cada segmento tiene su propia tabla de páginas, podremos tener un marco de
páginas incompleto al final de cada uno de los segmentos asignados a un proceso, lo que
implica una mayor fragmentación interna.

Memoria virtual.

Todos los métodos estudiados hasta el momento suponen que un proceso debe estar
completamente cargado en memoria para poder ejecutarse. Sin embargo, el modelo de Memoria
virtual aplica los mecanismos de intercambio que ya hemos mencionado para que las partes de
un proceso que no estén siendo utilizadas en un momento concreto, puedan residir en memoria
secundaria. De esta forma, se libera una mayor cantidad de memoria principal para albergar un
número de procesos superior. Además, este planteamiento nos permitiría ejecutar procesos que
fuesen más grandes que la memoria física.

Normalmente se implementa a partir de los conceptos de paginación y/o segmentación que hemos
estudiado antes.

Cuando el proceso hace referencia a una dirección de memoria que no reside en memoria
principal, se produce un fallo de página. En ese momento, el sistema localiza un marco de pagina
libre y carga en él la página necesaria. Si no hubiese marcos libres, habría que aplicar un algoritmo
de sustitución para elegir la página de este u otro proceso que debe abandonar la memoria
principal para dejar espacio a la que debe cargarse.

Mientras dura toda esta operación, el proceso que originó el fallo de página permanece en estado
Bloqueado.

Para saber qué páginas se encuentran en memoria principal y cuáles están en el disco, la tabla de
páginas puede incluir un bit de presencia.

Este tipo de esquemas consiguen que la memoria se aproveche mucho mejor. Además, al poder
cargar más procesos en la misma cantidad de memoria, el procesador estará mejor aprovechado.
Sin embargo, si se produce una situación que provoque una cantidad elevada de fallos de página,
los accesos a disco se multiplicarán y el rendimiento puede caer de forma considerable. Este
fenómeno recibe el nombre de hiperpaginación.

Administración de la memoria

Los sistemas operativos modernos suelen realizar la mayor parte de la administración de memoria
de forma automática y transparente. En cualquier caso, aún tenemos algunas cosas que podemos
hacer para mejorar el rendimiento de nuestro sistema.

La cantidad mínima de memoria para instalar Windows 8.1 es de 1 GB (en sistemas de 32 bits) o
2 GB (en sistemas de 64 bits.

Por su parte, Ubuntu 14.04 LTS necesita un mínimo de 384 MB

Evidentemente, lo primero será saber si tenemos la suficiente cantidad de memoria instalada. En


este sentido, debemos tener en cuenta que el mínimo recomendado por los fabricantes es eso, un
mínimo. En realidad, para un uso cotidiano del sistema, siempre se recomienda, como mínimo,
duplicar esa cantidad.

Otra precaución lógica será no ejecutar más programas de los necesarios en un determinado
instante. Un ejemplo habitual: Estás trabajando con el procesador de textos y necesitas hacer una
consulta en Internet. Abres el navegador, realizas la consulta y vuelves al procesador de textos…
Pero dejas abierto el navegador.

Si lo cierras, liberas la memoria que está ocupando para que puedan usarla otros programas y el
procesador, que no tendrá que dedicarle tiempo de proceso. Siempre podrás volver a abrirlo más
tarde.

Relacionado con lo anterior, conviene mencionar todos esos complementos que embellecen el
sistema, como las animaciones para abrir o cerrar ventanas, los relojes en el escritorio o los
complementos de la barra de tareas que nos indican la predicción del tiempo. Todos ellos se están
ejecutando en segundo plano y están consumiendo tiempo de procesador y capacidad de memoria,
lo que significa que, si tu ordenador no ofrece el rendimiento que esperas, estos deben ser los
primeros candidatos de los que prescindir.

Además de tener en cuenta los consejos básicos anteriores, también podemos aplicar algunas
acciones como las que ya te hemos explicamos en los siguientes artículos de SomeBooks.es:

Вам также может понравиться