2013-05-13 Estructura de Los Sistemas Operativos
2013-05-13 Estructura de Los Sistemas Operativos
2013-05-13 Estructura de Los Sistemas Operativos
Sistemas monolíticos
Sistemas en capas
Máquinas virtuales
Exokernels
Sistemas cliente-servidor.
La estructura consiste en que no hay estructura.
El sistema operativo se escribe como una colección
de procedimientos, cada uno de los cuales puede
llamar a cualquiera de los otros siempre que lo
necesite.
Cuando se utiliza esta técnica, cada procedimiento
del sistema tiene una interfaz bien definida desde
el punto de vista de los parámetros y resultados, y
cada uno está en libertad de llamar a cualquier
otro, si ese otro realiza alguna operación útil que el
primero necesita.
Cuando se adopta este enfoque, el programa
objeto del sistema operativo se construye
compilando primero todos los procedimientos
individuales, o ficheros que contienen los
procedimientos, para a continuación enlazarlos
todos en un único fichero objeto, utilizando el
enlazador del sistema.
En cuanto a la ocultación de la información,
esencialmente no hay ninguna ya que cualquier
procedimiento puede ver a cualquier otro (en
contraposición con una estructura que contiene
módulos o paquetes, en la que gran parte de la
información queda oculta dentro de los
módulos, y desde afuera sólo pueden invocarse
los puntos de entrada oficialmente declarados).
Sin embargo, incluso en los sistemas monolíticos
es posible tener al menos un poco de estructura.
Los servicios (llamadas al sistema)
proporcionados por el sistema operativo se
solicitan colocando los parámetros en un lugar
bien definido (la pila), y ejecutando después una
instrucción TRAP.
Esta instrucción hace que el procesador cambie de
modo usuario a modo núcleo y transfiere el
control al sistema operativo, lo cual se muestra
como el paso 6 en la Figura.
Entonces, el sistema operativo obtiene los
parámetros y determina qué llamada al sistema
debe ejecutarse.
Después de eso, utiliza el número de llamada al
sistema k como un índice para una tabla que
contiene en su entrada k un puntero al
procedimiento que lleva a cabo esa llamada
(paso 7 de la Figura).
Esta organización sugiere una estructura básica
para el sistema operativo:
1. Un programa principal que invoca el
procedimiento de servicio solicitado.
2. Un conjunto de procedimientos de servicio
que llevan a cabo las llamadas al sistema.
3. Un conjunto de procedimientos de utilidad
que sirven de ayuda a los procedimientos de
servicio.
En este modelo, por cada llamada al sistema
hay un procedimiento de servicio que se
encarga de ella.
Los procedimientos de utilidad hacen cosas
que son necesarias para varios procedimientos
de servicio, como obtener datos de los
programas de usuario.
Una generalización del enfoque de la Figura anterior
consiste en organizar el sistema operativo en una
jerarquía de capas, cada una construida sobre la que
está debajo.
El primer sistema construido de esta manera fue el
THE construido en la Technische Hogescholl
Eindhoven en los Países Bajos por E.W. Dijkstra (1968)
y sus estudiantes.
El sistema THE era un sencillo sistema por lotes para
un ordenador holandés, la Electrologica X8, que tenía
32K palabras de 27 bits (los bits eran costosos en aquel
entonces).
El sistema tenía seis capas, como se muestra en
la Figura.
La capa 0 se ocupaba de la asignación del
procesador, conmutando entre procesos según
tenían lugar las interrupciones o expiraban los
timers.
Por encima de la capa 0, el sistema consistía de
procesos secuenciales, cada uno de los cuales
podía programarse sin tener que preocuparse
por el hecho de que varios procesos estuvieran
ejecutándose en un único procesador.
En otras palabras, la capa 0 hacía posible la
multiprogramación básica de la CPU.
La capa 1 se encargaba de la administración de la
memoria.
Asignaba memoria a los procesos en la memoria
principal y sobre un tambor de 512K palabras en el
que se guardaban las partes de los procesos (páginas)
que no cabían en la memoria principal.
Por encima de la capa 1, los procesos no tenían que
preocuparse de saber si estaban en la memoria o en el
tambor; el software de la capa 1 se encargaba de que
las páginas se transfirieran a la memoria cuando se
necesitaban.
La capa 2 manejaba la comunicación entre cada
proceso y la consola del operador.
Por encima de esta capa cada proceso tenía
efectivamente su propia consola de operador.
La capa 3 se encargaba de gestionar los dispositivos de
E/S y de colocar búferes intermedios en los flujos de
información hacia y desde los dispositivos de E/S.
Encima de la capa 3 cada proceso podía tratar con
dispositivos de E/S abstractos con bonitas
propiedades, en lugar de dispositivos reales con
muchas peculiaridades.
En la capa 4 estaban los programas de usuario,
que no tenían que preocuparse por la gestión
de los procesos, la memoria, la consola o la
E/S.
El proceso del operador del sistema se
localizaba en la capa 5.
En el sistema MULTICS se hizo presente una
generalización adicional del concepto de
estructuración por capas.
En lugar de capas, el sistema MULTICS se describió
como si estuviera formado por una serie de anillos
concéntricos, teniendo los anillos interiores más
privilegios que los exteriores (lo que es efectivamente lo
mismo).
Cuando un procedimiento de un anillo exterior quería
llamar a un procedimiento de un anillo interior, tenía
que hacer el equivalente a una llamada al sistema, es
decir, una instrucción TRAP cuyos parámetros se
verificaban cuidadosamente para comprobar que fueran
válidos, antes de permitir que se efectuara la llamada.
Aunque en MULTICS todo el sistema operativo formaba
parte del espacio de direcciones de cada proceso de usuario,
el hardware permitía designar procedimientos individuales
(en realidad, segmentos de memoria) como protegidos
frente a lectura, escritura o ejecución.
Si bien el esquema de capas del sistema THE no era más
que una ayuda para el diseño, porque en última instancia
todas las partes del sistema se enlazaban en un único
programa objeto, en MULTICS el mecanismo de anillos sí
que estaba muy presente en tiempo de ejecución, estando
reforzado por el hardware de protección.
La ventaja del mecanismo de anillos es que
puede extenderse con facilidad para
estructurar los subsistemas de usuario.
Por ejemplo, un profesor puede escribir un
programa para testear y evaluar los programas
de los estudiantes y ejecutarlo en el anillo n,
mientras que los programas de usuario se
ejecutarían en el anillo n + 1 para que de
ninguna manera pudieran alterar sus
calificaciones.
Las primeras versiones de OS/360 fueron
estrictamente sistemas por lotes.
No obstante, muchos usuarios de las 360 deseaban
disponer de tiempo compartido, por lo que
diversos grupos, tanto dentro como fuera de IBM,
decidieron escribir sistemas de tiempo compartido
para esa máquina.
El sistema de tiempo compartido oficial de IBM, el
TSS/360, tardó mucho en entregarse, y cuando por
fin llegó era tan grande y lento que pocos sitios
adoptaron el nuevo sistema.
Eventualmente el sistema se abandonó después
de que su desarrollo hubiera consumido
alrededor de 50 millones de dólares (Graham,
1970).
No obstante, un grupo del Centro Científico de
IBM, en Cambridge, Massachussets, produjo
un sistema radicalmente distinto, que IBM
aceptó al final como producto, y que ahora se
utiliza ampliamente en los mainframes que
subsisten.
Este sistema, denominado originalmente
CP/CMS y rebautizado más adelante como
VM/370 (Seawright y MacKinnon, 1979), se
basaba en una astuta observación: un sistema
de tiempo compartido proporciona: (1)
multiprogramación y (2) una máquina
extendida con una interfaz más conveniente
que el hardware desnudo.
La esencia del VM/370 consiste en separar por
completo estas dos funciones.
El corazón del sistema, conocido como monitor
de máquina virtual, se ejecuta sobre el
hardware desnudo y realiza la
multiprogramación, proporcionando no una,
sino varias máquinas virtuales a la siguiente
capa inmediatamente superior, como se
muestra en la Figura.
Sin embargo, a diferencia de todos los demás
sistemas operativos, estas máquinas virtuales
no son máquinas extendidas, con ficheros y
otras características bonitas.
En vez de eso, son copias exactas del hardware
desnudo que incluyen el modo dual de
ejecución usuario/supervisor, E/S,
interrupciones y todo lo demás que tiene la
máquina real.
Dado que cada máquina virtual es idéntica al hardware
verdadero, cada una puede ejecutar cualquier sistema
operativo ejecutable directamente sobre el hardware desnudo.
Diferentes máquinas virtuales pueden ejecutar sistemas
operativos distintos, y a menudo lo hacen.
Algunas ejecutan uno de los descendientes del OS/360 para el
procesamiento por lotes o de transacciones, mientras que
otras ejecutan un sistema interactivo monousuario llamado
CMS (Conversational Monitor System; Sistema Monitor
Conversacional) para usuarios interactivos de tiempo
compartido.
Cuando un programa CMS ejecuta una llamada al
sistema, ésta salta (mediante un TRAP) al sistema
operativo en su propia máquina virtual, no al
VM/370, como haría si se estuviera ejecutando sobre
una máquina real, no virtual.
Luego el CMS ejecuta las instrucciones de E/S
normales para leer de su disco virtual, o lo que sea
que se necesite para llevar a cabo la llamada.
VM/370 atrapa estas instrucciones de E/S y luego las
ejecuta como parte de su simulación del hardware
real.
Al separar por completo las funciones de
multiprogramación y de proporcionar una máquina
extendida, cada una de las partes puede ser mucho más
sencilla, más flexible y más fácil de mantener.
El concepto de máquina virtual se utiliza mucho hoy en día
en un contexto diferente: la ejecución de programas MS-
DOS antiguos en un Pentium (u otra CPU Intel de 32 bits).
Al diseñar el Pentium y su software, tanto Intel como
Microsoft se percataron de que podría haber una gran
demanda de gente queriendo ejecutar su software antiguo
sobre el nuevo hardware.
Por ese motivo, Intel incluyó un modo 8086
virtual en el Pentium.
De este modo, la máquina actúa como un 8086
(que es idéntico a un 8088 desde el punto de vista
del software), incluyendo el direccionamiento de
16 bits con un límite de 1 MB.
Windows y otros sistemas operativos utilizan este
modo para ejecutar programas de MS-DOS.
Estos programas se inician en el modo 8086
virtual.
En tanto que ejecuten instrucciones normales, se ejecutan
sobre el hardware desnudo, pero cuando un programa
trate de saltar al sistema operativo para hacer una
llamada al sistema, o intente realizar E/S protegida
directamente, entonces tendrá lugar un salto (TRAP) al
monitor de máquina virtual.
Este diseño puede tener dos variantes.
En la primera, MS-DOS se carga en el espacio de
direcciones del 8086 virtual, de modo que lo único que
hace el monitor de máquina virtual es rebotar el salto a
MS-DOS, como sucedería en un 8086 real.
Cuando luego MS-DOS intente realizar la llamada él
mismo, la operación será capturada y llevada a cabo
por el monitor de la máquina virtual.
En la otra variante, el monitor de máquina virtual se
limita a atrapar el primer trap y a efectuar él mismo la
E/S, pues ya conoce todas las llamadas al sistema de
MS-DOS y, por tanto, sabe qué se supone que debe
hacer cada trap.
Esta variante es menos pura que la primera, puesto
que sólo emula correctamente a MS-DOS, y no a otros
sistemas operativos, como hace la primera.
Por otra parte, es mucho más rápida, pues ahorra el
trabajo de poner en marcha al MSDOS para que
realice la E/S.
Una desventaja adicional de ejecutar realmente MS-
DOS en modo 8086 virtual es que MS-DOS se mete
mucho con el bit que habilita/inhibe las
interrupciones, y la emulación de esto es muy costosa.
Es necesario resaltar que ninguno de estos enfoques
es en realidad igual al del VM/370, ya que la
máquina emulada no es un Pentium completo, sino
sólo un 8086.
Con el sistema VM/370 es posible ejecutar el propio
sistema VM/370 en la máquina virtual.
Con el Pentium no es posible ejecutar por ejemplo
Windows en el 8086 virtual, debido a que ninguna
versión de Windows se ejecuta sobre un 8086; un 286 es lo
mínimo que se necesita incluso para la versión más
antigua, y no se proporciona la emulación del 286 (y
mucho menos del Pentium).
No obstante, basta modificar un poco el binario de
Windows para hacer posible esta emulación, y de hecho
incluso está disponible en algunos productos comerciales.
Otro área donde se utilizan las máquinas virtuales,
pero de forma un tanto diferente, es en la
ejecución de programas en Java.
Cuando Sun Microsystems inventó el lenguaje de
programación Java, también inventó una máquina
virtual (es decir, una arquitectura de ordenador)
llamada JVM (Java Virtual Machine; Máquina
Virtual de Java).
El compilador de Java produce código para la
JVM, que normalmente es ejecutado por un
intérprete software de JVM.
La ventaja de este enfoque es que el código JVM puede
enviarse por Internet a cualquier ordenador que tenga un
intérprete de JVM y ejecutarse allí.
Si el compilador hubiera producido programas binarios
para SPARC o Pentium, por ejemplo, no se podrían haber
enviado y ejecutado en cualquier lugar tan fácilmente.
(Desde luego, Sun podría haber producido un compilador
que produjera binarios para SPARC y luego distribuir un
intérprete de SPARC, pero JVM es una arquitectura
mucho más sencilla que se presta muy bien a la
interpretación.)
Otra ventaja de usar JVM es que si el intérprete
se implementa como es debido, lo cual no es
del todo trivial, es posible verificar que los
programas JVM que lleguen sean seguros y
luego ejecutarlos bajo un entorno protegido de
forma que no puedan robar datos ni causar
ningún perjuicio.
Con el VM/370, cada proceso de usuario obtiene
una copia exacta del ordenador real.
Con el modo 8086 virtual del Pentium, cada
proceso de usuario obtiene una copia exacta de
un ordenador diferente.
Yendo un paso más lejos, algunos investigadores
del M.I.T. construyeron un sistema que
proporciona a cada usuario un clon del
ordenador real, pero con un subconjunto de los
recursos (Engler y otros, 1995).
Así, una máquina virtual podría obtener los
bloques de disco del 0 al 1023, la siguiente podría
recibir los bloques del 1024 al 2047, y así de forma
sucesiva.
En la capa más baja, ejecutándose en modo núcleo,
está un programa llamado exokernel.
Su labor consiste en asignar recursos a las
máquinas virtuales y luego comprobar cualquier
intento de utilizarlos para garantizar que ninguna
máquina trate de utilizar los recursos de cualquier
otra.
Cada máquina virtual a nivel de usuario puede ejecutar su
propio sistema operativo, como sobre el VM/370 y los
8086 virtuales del Pentium, sólo que cada una está limitada
a los recursos que solicitó y que le fueron asignados.
La ventaja del esquema de exokernel es que ahorra una
capa de conversión.
En los otros diseños, cada máquina virtual cree que tiene
su propio disco, cuyos bloques van desde 0 hasta algún
máximo, lo que obliga al monitor de la máquina virtual a
mantener tablas para convertir las direcciones de disco (y
todos los demás recursos).
Con el exokernel no es necesario efectuar esa
conversión, pues lo único que tiene que hacer es
mantenerse al tanto de qué recursos se han
asignado a qué máquinas virtuales.
Este método sigue teniendo la ventaja de separar
la multiprogramación (en el exokernel) y el
código del sistema operativo del usuario (en el
espacio del usuario), pero con menos sobrecarga
porque la única tarea del exokernel es evitar que
las máquinas virtuales se interfieran
mutuamente.
El VM/370 gana mucho en simplicidad al
mover una gran parte del código del sistema
operativo tradicional (la implementación de la
máquina extendida) a una capa superior, CMS.
No obstante, VM/370 sigue siendo él mismo
un programa complejo porque la simulación de
varias 370 virtuales en su totalidad no es tan
sencilla (sobre todo si se quiere hacer con una
eficiencia razonable).
Una tendencia en los sistemas operativos modernos
consiste en llevar más lejos aún la idea de subir código
a las capas superiores y quitar tanto como sea posible
del modo núcleo, dejando un microkernel mínimo.
El enfoque usual es implementar la mayor parte del
sistema operativo en procesos de usuario.
Para solicitar un servicio, tal como la lectura de un
bloque de un fichero, un proceso de usuario (que
ahora se denomina proceso cliente) envía una
solicitud a un proceso servidor, que realiza el trabajo y
devuelve la repuesta.
En este modelo, que se muestra en la Figura, lo único
que hace el núcleo es manejar la comunicación entre
clientes y servidores.
Al dividir el sistema operativo en partes, cada una de las
cuales sólo se encarga de una faceta del sistema, tales
como el servicio de ficheros, el servicio de procesos, el
servicio de terminal o el servicio de memoria, cada parte
se vuelve más pequeña y manejable.
Además, dado que todos los servidores se ejecutan como
procesos en modo usuario, no en modo núcleo, no tienen
acceso directo al hardware.
En consecuencia, si se produce un error en el
servidor de ficheros, podría fallar el servicio de
ficheros, pero usualmente ese error no llega a
provocar que se detenga toda la máquina.
Otra ventaja del modelo cliente-servidor es su
adaptabilidad para usarse en sistemas
distribuidos (vea la Figura).
Si un cliente se comunica con el servidor enviándole
mensajes, el cliente no necesita saber si el mensaje se
maneja de forma local en su propia máquina, o si se
envió a través de la red a un servidor situado en una
máquina remota.
En lo que concierne al cliente, en ambos casos sucede lo
mismo: se envió una solicitud y se recibió una respuesta.
La descripción presentada anteriormente de un núcleo
que tan solo se encarga de transportar mensajes desde
los clientes a los servidores y al revés, no es del todo
realista.
Algunas funciones del sistema operativo (como la carga
de comandos en los registros de los dispositivos de E/S
físicos) son difíciles, sino imposibles, de llevar a cabo
desde programas en el espacio del usuario.
Hay dos formas de resolver este problema.
Una es hacer que algunos procesos servidores cruciales
(por ejemplo, los drivers de dispositivo) se ejecuten
realmente en modo núcleo, con un acceso sin
restricciones a todo el hardware, pero manteniendo
todavía su comunicación con los otros procesos,
empleando el mecanismo normal de mensajes.
La otra forma es incorporar un mínimo de
mecanismo en el núcleo pero dejar las
decisiones de política a los servidores en el
espacio de usuario (Levin y otros, 1975).
Por ejemplo, el núcleo podría reconocer que un
mensaje enviado a una cierta dirección especial
implica tomar el contenido de ese mensaje y
cargarlo en los registros de dispositivo de E/S
de algún disco, a fin de iniciar una lectura del
disco.
En este ejemplo, el núcleo ni siquiera examinaría
los bytes del mensaje para ver si son válidos o
lógicos; tan solo los copiaría a ciegas en los
registros de dispositivo del disco.
(Obviamente tendría que utilizarse algún
esquema para limitar tales mensajes a los
procesos autorizados.)
La división entre mecanismo y política es un
concepto importante; se presenta una y otra vez
en los sistemas operativos en diversos contextos.