Sistemas Operativos
Indice
1.
Introducción
2. Tipos de Sistemas
Operativos
3. Sistemas de Archivos
4.
Administración de la Memoria
5. Administración de
Procesos
7. Núcleos de
Sistemas Operativos
8.
Caso de Estudio: UNIX
9.
Caso de Estudio: VMS
10.
Caso de Estudio: OS/2
11.
Caso de Estudio: WindowsNT
12.
Caso de Estudio: Procesos en Linux
13.
Bibliografía.
1. Introducción
A finales de los 40's el uso de computadoras estaba restringido a aquellas empresas o instituciones que podían pagar su alto precio, y no existían los sistemas operativos. En su lugar, el programador debía tener un conocimiento y contacto profundo con el hardware, y en el infortunado caso de que su programa fallara, debía examinar los valores de los registros y páneles de luces indicadoras del estado de la computadora para determinar la causa del fallo y poder corregir su programa, además de enfrentarse nuevamente a los procedimientos de apartar tiempo del sistema y poner a punto los compiladores, ligadores, etc; para volver a correr su programa, es decir, enfrentaba el problema del procesamiento serial ( serial processing ).
La importancia de los sistemas operativos nace históricamente desde los 50's, cuando se hizo evidente que el operar una computadora por medio de tableros enchufables en la primera generación y luego por medio del trabajo en lote en la segunda generación se podía mejorar notoriamente, pues el operador realizaba siempre una secuencia de pasos repetitivos, lo cual es una de las características contempladas en la definición de lo que es un programa. Es decir, se comenzó a ver que las tareas mismas del operador podían plasmarse en un programa, el cual a través del tiempo y por su enorme complejidad se le llamó "Sistema Operativo". Así, tenemos entre los primeros sistemas operativos al Fortran Monitor System ( FMS ) e IBSYS .
Posteriormente, en la tercera generación de computadoras nace uno de los primeros sistemas operativos con la filosofía de administrar una familia de computadoras: el OS/360 de IBM. Fue este un proyecto tan novedoso y ambicioso que enfrentó por primera vez una serie de problemas conflictivos debido a que anteriormente las computadoras eran creadas para dos propósitos en general: el comercial y el científico. Así, al tratar de crear un solo sistema operativo para computadoras que podían dedicarse a un propósito, al otro o ambos, puso en evidencia la problemática del trabajo en equipos de análisis, diseño e implantación de sistemas grandes. El resultado fue un sistema del cual uno de sus mismos diseñadores patentizó su opinión en la portada de un libro: una horda de bestias prehistóricas atascadas en un foso de brea.
Surge también en la tercera generación de computadoras el concepto de la multiprogramación, porque debido al alto costo de las computadoras era necesario idear un esquema de trabajo que mantuviese a la unidad central de procesamiento más tiempo ocupada, así como el encolado (spooling ) de trabajos para su lectura hacia los lugares libres de memoria o la escritura de resultados. Sin embargo, se puede afirmar que los sistemas durante la tercera generación siguieron siendo básicamente sistemas de lote.
En la cuarta generación la electrónica avanza hacia la integración a gran escala, pudiendo crear circuitos con miles de transistores en un centímetro cuadrado de silicón y ya es posible hablar de las computadoras personales y las estaciones de trabajo. Surgen los conceptos de interfaces amigables intentando así atraer al público en general al uso de las computadoras como herramientas cotidianas. Se hacen populares el MS-DOS y UNIX en estas máquinas. También es común encontrar clones de computadoras personales y una multitud de empresas pequeñas ensamblándolas por todo el mundo.
Para mediados de los 80's, comienza el auge de las redes de computadoras y la necesidad de sistemas operativos en red y sistemas operativos distribuidos. La red mundial Internet se va haciendo accesible a toda clase de instituciones y se comienzan a dar muchas soluciones ( y problemas ) al querer hacer convivir recursos residentes en computadoras con sistemas operativos diferentes. Para los 90's el paradigma de la programación orientada a objetos cobra auge, así como el manejo de objetos desde los sistemas operativos. Las aplicaciones intentan crearse para ser ejecutadas en una plataforma específica y poder ver sus resultados en la pantalla o monitor de otra diferente (por ejemplo, ejecutar una simulación en una máquina con UNIX y ver los resultados en otra con DOS ). Los niveles de interacción se van haciendo cada vez más profundos.
2. Tipos de sistemas operativos
En esta sección se describirán las características que clasifican a los sistemas operativos, básicamente se cubrirán tres clasificaciones: sistemas operativos por su estructura (visión interna), sistemas operativos por los servicios que ofrecen y, finalmente, sistemas operativos por la forma en que ofrecen sus servicios (visión externa).
Sistemas Operativos por su Estructura
Según [Alcal92], se deben observar dos tipos de requisitos
cuando se construye un sistema operativo, los cuales son:
Requisitos de usuario: Sistema fácil de usar y de
aprender, seguro,
rápido y adecuado al uso al que se le quiere destinar.
Requisitos del software: Donde se engloban
aspectos como el mantenimiento,
forma de operación, restricciones de uso, eficiencia,
tolerancia
frente a los errores y flexibilidad.
A continuación se describen las distintas estructuras
que presentan los actuales sistemas operativos para satisfacer
las necesidades que de ellos se quieren obtener.
Estructura monolítica.
Es la estructura de
los primeros sistemas operativos constituídos
fundamentalmente por un solo programa compuesto de un conjunto de
rutinas entrelazadas de tal forma que cada una puede llamar a
cualquier otra (Ver Fig. 2). Las características
fundamentales de este tipo de estructura son:
Construcción del programa final a base de
módulos compilados separadamente que se unen a
través del ligador.
Buena definición de parámetros de enlace entre las
distintas rutinas existentes, que puede provocar mucho
acoplamiento.
Carecen de protecciones y privilegios al entrar a rutinas que
manejan diferentes aspectos de los recursos de
la
computadora, como memoria, disco,
etc.
Generalmente están hechos a medida, por lo que son
eficientes y rápidos en su ejecución y gestión, pero por lo mismo carecen de
flexibilidad para soportar diferentes ambientes de trabajo o
tipos de aplicaciones.
Estructura jerárquica.
A medida que fueron creciendo las necesidades de los usuarios y
se perfeccionaron los sistemas, se hizo necesaria una mayor
organización del software, del sistema
operativo, donde una parte del sistema contenía subpartes
y esto organizado en forma de niveles.
Se dividió el sistema operativo en pequeñas partes, de tal forma que cada una de ellas estuviera perfectamente definida y con un claro interface con el resto de elementos.
Se constituyó una estructura jerárquica o de niveles en los sistemas operativos, el primero de los cuales fue denominado THE (Technische Hogeschool, Eindhoven), de Dijkstra, que se utilizó con fines didácticos (Ver Fig. 3). Se puede pensar también en estos sistemas como si fueran `multicapa'. Multics y Unix caen en esa categoría.
En la estructura anterior se basan prácticamente la
mayoría de los sistemas operativos actuales. Otra forma de
ver este tipo de sistema es la denominada de anillos
concéntricos o "rings" (Ver Fig. 4).
En el sistema de anillos, cada uno tiene una apertura, conocida
como puerta o trampa (trap), por donde pueden entrar las llamadas
de las capas inferiores. De esta forma, las zonas más
internas del sistema operativo o núcleo del sistema
estarán más protegidas de accesos indeseados desde
las capas más externas. Las capas más internas
serán, por tanto, más privilegiadas que las
externas.
Máquina Virtual.
Se trata de un tipo de sistemas operativos que presentan una
interface a cada proceso,
mostrando una máquina que parece idéntica a la
máquina real subyacente. Estos sistemas operativos separan
dos conceptos que suelen estar unidos en el resto de sistemas: la
multiprogramación y la máquina extendida. El
objetivo de
los sistemas operativos de máquina virtual es el de
integrar distintos sistemas operativos dando la sensación
de ser varias máquinas
diferentes.
El núcleo de estos sistemas operativos se denomina monitor virtual y tiene como misión llevar a cabo la multiprogramación, presentando a los niveles superiores tantas máquinas virtuales como se soliciten. Estas máquinas virtuales no son máquinas extendidas, sino una réplica de la máquina real, de manera que en cada una de ellas se pueda ejecutar un sistema operativo diferente, que será el que ofrezca la máquina extendida al usuario (Ver Fig. 5).
Cliente-servidor (
Microkernel)
El tipo más reciente de sistemas operativos es el
denominado Cliente-servidor, que
puede ser ejecutado en la mayoría de las computadoras, ya
sean grandes o pequeñas.
Este sistema sirve para toda clase de aplicaciones por tanto, es
de propósito general y cumple con las mismas actividades
que los sistemas operativos convencionales.
El núcleo tiene como misión
establecer la
comunicación entre los clientes y los
servidores.
Los procesos
pueden ser tanto servidores como
clientes. Por
ejemplo, un programa de aplicación normal es un cliente que llama
al servidor correspondiente para acceder a un archivo o
realizar una operación de entrada/salida sobre un
dispositivo concreto. A su
vez, un proceso
cliente puede actuar como servidor para otro." [Alcal92]. Este
paradigma
ofrece gran flexibilidad en cuanto a los servicios posibles en el
sistema final, ya que el núcleo provee solamente funciones muy
básicas de memoria, entrada/salida, archivos y
procesos,
dejando a los servidores proveer la mayoría que el usuario
final o programador puede usar. Estos servidores deben tener
mecanismos de seguridad y
protección que, a su vez, serán filtrados por el
núcleo que controla el hardware. Actualmente se
está trabajando en una versión de UNIX que
contempla en su diseño
este paradigma.
Sistemas Operativos por Servicios
Esta clasificación es la más comúnmente
usada y conocida desde el punto de vista del usuario final. Esta
clasificación se comprende fácilmente con el cuadro
sinóptico que a continuación se muestra en la
Fig. 6.
Monousuarios
Los sistemas operativos monousuarios son aquéllos que
soportan a un usuario a la vez, sin importar el número de
procesadores que
tenga la computadora o
el número de procesos o tareas que el usuario pueda
ejecutar en un mismo instante de tiempo. Las computadoras
personales típicamente se han clasificado en este
renglón.
Multiusuarios
Los sistemas operativos multiusuarios son capaces de dar servicio a
más de un usuario a la vez, ya sea por medio de varias
terminales conectadas a la computadora o por medio de sesiones
remotas en una red de comunicaciones. No importa el número de
procesadores en
la máquina ni el número de procesos que cada
usuario puede ejecutar simultáneamente.
Monotareas
Los sistemas monotarea son aquellos que sólo permiten una
tarea a la vez por usuario. Puede darse el caso de un sistema
multiusuario y monotarea, en el cual se admiten varios usuarios
al mismo tiempo pero cada uno de ellos puede estar haciendo solo
una tarea a la vez.
Multitareas
Un sistema operativo multitarea es aquél que le permite al
usuario estar realizando varias labores al mismo tiempo. Por
ejemplo, puede estar editando el código
fuente de un programa durante su depuración mientras
compila otro programa, a la vez que está recibiendo
correo
electrónico en un proceso en background. Es
común encontrar en ellos interfaces gráficas orientadas al uso de menús
y el ratón, lo cual permite un rápido intercambio
entre las tareas para el usuario, mejorando su productividad.
Uniproceso
Un sistema operativo uniproceso es aquél que es capaz de
manejar solamente un procesador de la
computadora, de manera que si la computadora tuviese más
de uno le sería inútil. El ejemplo más
típico de este tipo de sistemas es el DOS y MacOS.
Multiproceso
Un sistema operativo multiproceso se refiere al número de
procesadores del sistema, que es más de uno y éste
es capaz de usarlos todos para distribuir su carga de trabajo.
Generalmente estos sistemas trabajan de dos formas:
simétrica o asimétricamente. Cuando se trabaja de
manera asimétrica, el sistema operativo selecciona a uno
de los procesadores el cual jugará el papel de
procesador
maestro y servirá como pivote para distribuir la carga a
los demás procesadores, que reciben el nombre de esclavos.
Cuando se trabaja de manera simétrica, los procesos o
partes de ellos (threads) son enviados indistintamente a
cualesquira de los procesadores disponibles, teniendo,
teóricamente, una mejor distribución y equilibrio en
la carga de trabajo bajo este esquema.
Se dice que un thread es la parte activa en memoria y corriendo de un proceso, lo cual puede consistir de un área de memoria, un conjunto de registros con valores específicos, la pila y otros valores de contexto. Us aspecto importante a considerar en estos sistemas es la forma de crear aplicaciones para aprovechar los varios procesadores. Existen aplicaciones que fueron hechas para correr en sistemas monoproceso que no toman ninguna ventaja a menos que el sistema operativo o el compilador detecte secciones de código paralelizable, los cuales son ejecutados al mismo tiempo en procesadores diferentes. Por otro lado, el programador puede modificar sus algoritmos y aprovechar por sí mismo esta facilidad, pero esta última opción las más de las veces es costosa en horas hombre y muy tediosa, obligando al programador a ocupar tanto o más tiempo a la paralelización que a elaborar el algoritmo inicial.
Sistemas Operativos por la Forma de Ofrecer sus
Servicios
Esta clasificación también se refiere a una
visión externa, que en este caso se refiere a la del
usuario, el cómo accesa los servicios. Bajo esta
clasificación se pueden detectar dos tipos principales:
sistemas operativos de red y sistemas operativos
distribuídos.
Sistemas Operativos de Red
Los sistemas operativos de red se definen como aquellos que tiene
la capacidad de interactuar con sistemas operativos en otras
computadoras por medio de un medio de transmisión con el
objeto de intercambiar información, transferir archivos,
ejecutar comandos remotos
y un sin fin de otras actividades. El punto crucial de estos
sistemas es que el usuario debe saber la sintaxis de un cinjunto
de comandos o
llamadas al sistema para ejecutar estas operaciones,
además de la ubicación de los recursos que desee
accesar. Por ejemplo, si un usuario en la computadora hidalgo
necesita el archivo matriz.pas que
se localiza en el directorio /software/codigo en la
computadora morelos bajo el sistema operativo UNIX, dicho usuario
podría copiarlo a través de la red con los comandos
siguientes: hidalgo% hidalgo% rcp morelos:/software/codigo/matriz.pas .
hidalgo%. En este caso, el comando rcp que significa "remote
copy" trae el archivo indicado de la computadora morelos y lo
coloca en el directorio donde se ejecutó el mencionado
comando. Lo importante es hacer ver que el usuario puede accesar
y compartir muchos recursos.
Sistemas Operativos Distribuídos
Los sistemas operativos distribuídos abarcan los servicios
de los de red, logrando integrar recursos ( impresoras,
unidades de respaldo, memoria, procesos, unidades centrales de
proceso ) en una sola máquina virtual que el usuario
accesa en forma transparente. Es decir, ahora el usuario ya no
necesita saber la ubicación de los recursos, sino que los
conoce por nombre y simplementa los usa como si todos ellos
fuesen locales a su lugar de trabajo habitual. Todo lo anterior
es el marco
teórico de lo que se desearía tener como
sistema operativo distribuído, pero en la realidad no se
ha conseguido crear uno del todo, por la complejidad que suponen:
distribuír los procesos en las varias unidades de
procesamiento, reintegrar sub-resultados, resolver problemas de
concurrencia y paralelismo, recuperarse de fallas de algunos
recursos distribuídos y consolidar la protección y
seguridad entre
los diferentes componentes del sistema y los usuarios. Los
avances
tecnológicos en las redes de área local y
la creación de microprocesadores
de 32 y 64 bits lograron que computadoras mas o menos baratas
tuvieran el suficiente poder en forma autónoma para
desafiar en cierto grado a los mainframes, y a la vez se dio la
posibilidad de intercomunicarlas, sugiriendo la oportunidad de
partir procesos muy pesados en cálculo en
unidades más pequeñas y distribuirlas en los varios
microprocesadores para luego reunir los
sub-resultados, creando así una máquina virtual en
la red que exceda en poder a un mainframe. El sistema integrador
de los microprocesadores que hacer ver a las varias memorias,
procesadores, y todos los demás recursos como una sola
entidad en forma transparente se le llama sistema operativo
distribuído. Las razones para crear o adoptar sistemas
distribuídos se dan por dos razones principales: por
necesidad ( debido a que los problemas a resolver son
inherentemente distribuídos ) o porque se desea tener
más confiabilidad y disponibilidad de recursos. En el
primer caso tenemos, por ejemplo, el control de los
cajeros automáticos en diferentes estados de la
república. Ahí no es posible ni eficiente mantener
un control
centralizado, es más, no existe capacidad de
cómputo y de entrada/salida para dar servicio a los
millones de operaciones por
minuto. En el segundo caso, supóngase que se tienen en una
gran empresa varios
grupos de
trabajo, cada uno necesita almacenar grandes cantidades de
información en disco duro con
una alta confiabilidad y disponibilidad. La solución puede
ser que para cada grupo de
trabajo se asigne una partición de disco duro en
servidores diferentes, de manera que si uno de los servidores
falla, no se deje dar el servicio a todos, sino sólo a
unos cuantos y, más aún, se podría tener un
sistema con discos en espejo ( mirror ) a través de la
red,de manera que si un servidor se cae, el servidor en espejo
continúa trabajando y el usuario ni cuenta se da de estas
fallas, es decir, obtiene acceso a recursos en forma
transparente.
Ventajas de los Sistemas Distribuídos
En general, los sistemas distribuídos (no solamente los
sistemas operativos) exhiben algunas ventajas sobre los sistemas
centralizados que se describen enseguida.
Por otro lado, los sistemas distribuídos también exhiben algunas ventajas sobre sistemas aislados. Estas ventajas son:
Desventajas de los Sistemas Distribuídos
Así como los sistemas distribuídos exhiben grandes
ventajas, también se pueden identificar algunas
desventajas, algunas de ellas tan serias que han frenado la
producción comercial de sistemas operativos
en la actualidad. El problema más importante en la
creación de sistemas distribuídos es el software:
los problemas de compartición de datos y recursos es tan
complejo que los mecanismos de solución generan mucha
sobrecarga al sistema haciéndolo ineficiente. El checar,
por ejemplo, quiénes tienen acceso a algunos recursos y
quiénes no, el aplicar los mecanismos de protección
y registro de
permisos consume demasiados recursos. En general, las soluciones
presentes para estos problemas están aún en
pañales.
Otros problemas de los sistemas operativos distribuídos surgen debido a la concurrencia y al paralelismo. Tradicionalmente las aplicaiones son creadas para computadoras que ejecutan secuencialmente, de manera que el identificar secciones de código `paralelizable' es un trabajo ardúo, pero necesario para dividir un proceso grande en sub-procesos y enviarlos a diferentes unidades de procesamiento para lograr la distribución. Con la concurrencia se deben implantar mecanismos para evitar las condiciones de competencia, las postergaciones indefinidas, el ocupar un recurso y estar esperando otro, las condiciones de espera circulares y , finalmente, los "abrazos mortales" (deadlocks). Estos problemas de por sí se presentan en los sistemas operativos multiusuarios o multitareas, y su tratamiento en los sistemas distribuídos es aún más complejo, y por lo tanto, necesitará de algoritmos más complejos con la inherente sobrecarga esperada.
Por otro lado, en el tema de sistemas distribuídos existen varios conceptos importantes referentes al hadware que no se ven en este trabajo: multicomputadoras, multiprocesadores, sistemas acoplados débil y fuertemente, etc. En páginas 366 - 376 puede encontrarse material relacionado a estos conceptos.
3. Tipos de sistemas de archivos
Un sistema de archivos ( file system ) es una estructura de directorios con algún tipo de organización el cual nos permite almacenar, crear y borrar archivos en diferenctes formatos. En esta sección se revisarán conceptos importantes relacionados a los sistemas de archivos.
Almacenamiento Físico de Datos
En un sistema de cómputo es evidente que existe la
necesidad por parte de los usuarios y aplicaciones de almacenar
datos en algún medio, a veces por periodos largos y a
veces por instantes. cada aplicación y cada usuario debe
tener ciertos derechos con sus datos, como
son el poder crearlos y borrarlos, o cambialos de lugar;
así como tener privacidad contra otros usuarios o
aplicaciones. El subsistema de archivos del sistema operativo se
debe encargar de estos detalles, además de establecer el
formato físico en el cual almacenará los datos en
discos duros,
cintas o discos flexibles. Debe ser conocido por todos que
tradicionalmente la información en los sistemas modernos
se almacena en discos duros,
flexibles y unidades de disco óptico, y en todos ellos se
comparten algunos esquemas básicos para darles formato
físico: las superficies de almacenamiento
son divididas en círculos concéntricos llamados
"pistas" y cada pista se divide en "sectores". A la unión
lógica
de varias pistas a través de varias superficies
"paralelas" de almacenamiento se
les llama "cilindros", los cuales son inspeccionados al momento
de lectura o
escritura de
datos por las respectivas unidades fisicas llamadas "cabezas".
Las superficies de almacenamiento reciben el nombre de "platos" y
generalmente están en movimiento
rotatorio para que las cabezas accesen a las pistas que los
componen. Los datos se escriben a través de los sectores
en las pistas y cilindros modificando las superficies por medio
de las cabezas.
El tiempo que una cabeza se tarda en ir de una pista a otra se le llama "tiempo de búsqueda" y dependerá de la distancia entre la posición actual y la distancia a la pista buscada. El tiempo que tarda una cabeza en ir del sector actual al sector deseado se le llama tiempo de latencia y depende de la distancia entre sectores y la velocidad de rotación del disco. El impacto que tiene las lecturas y escrituras sobre el sistema está determinado por la tecnología usada en los platos y cabezas y por la forma de resolver las peticiones de lectura y escritura, es decir, los algoritmos de planificación.
Algoritmos de planificación de peticiones
Los algoritmos de planificación de peticiones de lectura y
escritura a discos se encargan de registrar dichas peticiones y
de responderlas en un tiempo razonable. Los algoritmos más
comunes para esta tarea son:
Asignación del espacio de almacenamiento
El subsistema de archivos se debe encargar de localizar espacio
libre en los medios de
almacenamiento para guardar archivos y para después
borrarlos, renombrarlos o agrandarlos. Para ello se vale de
localidades especiales que contienen la lista de archivos creados
y por cada archivo una serie de direcciones que contienen los
datos de los mismos. Esas localidades especiales se llaman
directorios. Para asignarle espacio a los archivos existen tres
criterios generales que se describen enseguida.
Métodos de acceso en los sistemas de archivos.
Los métodos de
acceso se refiere a las capacidades que el subsistema de archivos
provee para accesar datos dentro de los directorios y medios de
almacenamiento en general. Se ubican tres formas generales:
acceso secuencial, acceso directo y acceso directo indexado.
Operaciones soportadas por el subsistema de archivos
Independientemente de los algoritmos de asignación de
espacio, de los métodos de
acceso y de la forma de resolver las peticiones de lectura y
escritura, el subsistema de archivos debe proveer un conjunto de
llamadas al sistema para operar con los datos y de proveer
mecanismos de protección y seguridad. Las operaciones
básicas que la mayoría de los sistemas de archivos
soportan son:
Algunas facilidades extras de los sistemas de archivos
Algunos sistemas de archivos proveen herramientas
al administrador del
sistema para facilitarle la vida. Las más notables es la
facilidad de compartir archivos y los sistemas de `cotas'.
La facilidad de compartir archivos se refiere a la posibilidad de que los permisos de los archivos o directorios dejen que un grupo de usuarios puedan accesarlos para diferentes operaciones" leer, escribir, borrar, crear, etc. El dueño verdadero es quien decide qué permisos se aplicarán al grupo e, incluso, a otros usuarios que no formen parte de su grupo. La facilidad de `cotas' se refiere a que el sistema de archivos es capaz de llevar un control para que cada usuario pueda usar un máximo de espacio en disco duro. Cuando el usuario excede ese límite, el sistema le envía un mensaje y le niega el permiso de seguir escribiendo, obligándolo a borrar algunos archivos si es que quiere almacenar otros o que crezcan. La versión de UNIX SunOS contiene esa facilidad.
Sistemas de Archivos Aislados
Los sistemas de archivos aislados son aquellos que residen en una
sola computadora y no existe la posibilidad de que, aún
estando en una red, otros sistemas
puedan usar sus directorios y archivos. Por ejemplo, los archivos
en discos duros en el sistema MS-DOS
clásico se puede ver en esta categoría.
Sistemas de Archivos Compartidos o de Red
Estos sistemas de archivos es factible accesarlos y usarlos desde
otros nodos en una red. Generalmente existe un `servidor' que es
la computadora en donde reside el sistema de archivos
físicamente, y por otro lado están los `clientes',
que se valen del servidor para ver sus archivos y directorios de
manera como si estuvieran localmente en el cliente. Algunos
autores les llaman a estos sistemas de archivos `sistemas de
archivos distribuídos' lo cual no se va a discutir en este
trabajo.
Los sistemas de archivos compartidos en red más populares son los provistos por Netware, el Remote Filke Sharing ( RFS en UNIX ), Network File System ( NFS de Sun Microsystems ) y el Andrew File System ( AFS ). En general, lo que proveen los servidores es un medio de que los clientes, localmente, realicen peticiones de operaciones sobre archivos los cuales con `atrapadas' por un `driver' o un `módulo' en el núcleo del sistema operativo, el cual se comunica con el servidor a través de la red y la operación se ejecuta en el servidor. Existen servidores de tipo "stateless y no-stateless". Un servidor "stateless" no registra el estado de las operaciones sobre los archivos, de manera que el cliente se encarga de todo ese trabajo. La ventaja de este esquema es que si el servidor falla, el cliente no perderá información ya que ésta se guarda en memoria localmente, de manera que cuando el servidor reanude su servicio el cliente proseguirá como si nada hubiese sucedido. Con un servidor "no-stateless", esto no es posible.
La protección sobre las operaciones se lleva a cabo tanto el los clientes como en el servidor: si el usuario quiere ejecutar una operación indebida sobre un archivo, recibirá un mensaje de error y posiblemente se envíe un registro al subsistema de `seguridad' para informar al administrador del sistema de dicho intento de violación.
En la práctica, el conjunto de permisos que cada usuario tiene sobre el total de archivos se almacena en estructuras llamadas `listas de acceso' ( access lists ).
Tendencias actuales
Con el gran auge de las redes de comunicaciones
y su incremento en el ancho de banda, la proliferación de
paquetes que ofrecen la compartición de archivos es
común. Los esquemas más solicitados en la industria es
el poder accesar los grandes volúmenes de
información que residen en grandes servidores desde las
computadoras personales y desde otros servidores también.
Es una realidad que la solución más socorrida en
las empresas pequeñas es usar Novell Netware
en un servidor 486 o superior y accesar los archivos desde
máquinas similares.
A veces se requieren soluciones más complejas con
ambientes heterogéneos:
diferentes sistemas operativos y diferentes arquitecturas. Uno de
los sistemas de archivos más expandidos en estaciones de
trabajo es el NFS, y prácticamente todas las versiones de
UNIX traen instalado un cliente y hasta un servidor de este
servicio. Es posible así que una gran cantidad de
computadoras personales (de 10 a 80 ) accesen grandes
volúmenes de información o paquetería (desde
1 a 8 Gygabytes ) desde una sola estación de trabajo, e
incluso tener la flexibilidad de usar al mismo tiempo servidores
de Novell y NFS.
Soluciones similares se dan con algunos otros paquetes
comerciales, pero basta ya de `goles'. Lo importante aquí
es observar que el mundo se va moviendo poco a poco hacia
soluciones distribuídas, y hacia la estandarización
que, muchas veces, es `de facto'.
4. Administracion de la memoria
En esta sección se describirán las técnicas más usuales en el manejo de memoria, revisando los conceptos relevantes. Se abarcarán los esquemas de manejo simple de memoria real, la multiprogramación en memoria real con sus variantes, el concepto de `overlays', la multiprogramación con intercambio y los esquemas de manejo de memoria virtual.
Panorama general
Un vistazo al material que se va a cubrir en esta sección
se muestra en la
figura 4.1. Es una gráfica en donde se especifican, en
términos generales, los conceptos más importantes
en cuanto a las técnicas
empleadas en el manejo de memoria.
Manejo de memoria en sistemas monousuario sin intercambio
Este esquema es aún muy frecuente en México y
se usa principalmente en sistemas monousuario y monotarea, como
son las computadoras personales con DOS. Bajo este esquema, la
memoria real es tomada para almacenar el programa que se
esté ejecutando en un momento dado, con la visible
desventaja de que si se está limitado a la cantidad de
RAM disponible
únicamente. La organización física bajo este
esquema es muy simple: El sistema operativo se ubica en las
localidades superiores o inferiores de la memoria, seguido por
algunos manejadores de dispositivos ( `drivers' ). Esto deja un
espacio contiguo de memoria disponible que es tomado por los
programas del
usuario, dejando generalmente la ubicación de la pila (`
stack' ) al último, con el objetivo de
que ésta pueda crecer hasta el máximo posible.
Estas diferentes opciones se pueden ver en la figura 4.2. Como es
obvio, bajo estos esquemas no se requieren algoritmos
sofisticados para asignar la memoria a los diferentes procesos,
ya que éstos son ejecutados secuencialmente conforme van
terminando.
Multiprogramación en memoria real
En los 60's, las empresas e instituciones que habían
invertido grandes sumas en la compra de equipo de cómputo
se dieron cuenta rápidamente que los sistemas en lote
invertían una gran cantidad de tiempo en operaciones de
entrada y salida, donde la intervención de la unidad
central de procesamiento era prácticamente nula, y se
comenzaron a preguntar cómo hacer que se mantuviera
más tiempo ocupada. Fue así como nació el
concepto de multiprogramación, el cual consiste en la idea
de poner en la memoria física más de un proceso al
mismo tiempo, de manera que si el que se está ejecutando
en este momento entraba en un periodo de entrada/salida, se podia
tomar otro proceso para que usara la unidad central de
procesamiento. De esta forma, la memoria fisica se dividía
en secciones de tamaño suficiente para contener a varios
programas.
De esta manera, si un sistema gastaba en promedio 60% de su tiempo en entrada/salida por proceso, se podía aprovechar más el CPU. Anterior a esto, el CPU se mantenía ese mismo porcentaje ocioso; con la nueva técnica, el tiempo promedio ocioso disminuye de la siguiente forma. Llámese al tiempo promedio que el CPU está ocupado `grado de multiprogramación'. Si el sistema tuviese un solo proceso siempre, y éste gastara 60% en entrada/salida, el grado de multiprogramación sería 1 - 60% = 40% = 0.4. Con dos procesos, para que el CPU esté ocioso se necesita que ambos procesos necesiten estar haciendo entrada/salida, es decir, suponiendo que son independientes, la probabilidad de que ambos estén en entrada/salida es el producto de sus probabilidades, es decir, 0.6x0.6 = 0.36. Ahora, el grado de multiprogramación es 1 - (probabilidad de que ambos procesos estén haciendo entrada/salida) = 1 - 0.36 = 0.64.
Como se ve, el sistema mejora su uso de CPU en un 24% al aumentar de uno a dos procesos. Para tres procesos el grado de multiprogramación es 1 - (0.6) 3 = 0.784, es decir, el sistema está ocupado el 78.4% del tiempo. La fórmula del grado de multiprogramación, aunque es muy idealista, pudo servir de guía para planear un posible crecimiento con la compra de memoria real, es decir, para obtener el punto en que la adición de procesos a RAM ya no incrementa el uso de CPU.
Dentro del esquema de multiprogramación en memoria real surgieron dos problemas interesantes: la protección y la relocalización.
El problema de la relocalización
Este problema no es exclusivo de la multiprogramación en
memoria real, sino que se presentó aquí pero se
sigue presentando en los esquemas de memoria
virtual también. Este problema consiste en que los
programas que necesitan cargarse a memoria real ya están
compilados y ligados, de manera que internamente contienen una
serie de referencias a direcciones de instrucciones, rutinas y
procedimientos
que ya no son válidas en el espacio de direcciones de
memoria real de la sección en la que se carga el programa.
Esto es, cuando se compiló el programa se definieron o
resolvieron las direcciones de memoria de acuerdo a la
sección de ese momento, pero si el programa se carga en
otro dia en una sección diferente, las direcciones reales
ya no coinciden. En este caso, el manejador de memoria puede
solucionar el problema de dos maneras: de manera `estática'
o de manera `dinámica'. La solución `estática'
consiste en que todas las direcciones del programa se vuelvan a
recalcular al momento en que el programa se carga a memoria, esto
es, prácticamente se vuelve a recompilar el programa. La
solución `dinámica' consiste en tener un registro que
guarde la dirección base de la sección que va a
contener al programa. Cada vez que el programa haga una
referencia a una dirección de memoria, se le suma el
registro base para encontrar la dirección real. Por
ejemplo, suponga que el programa es cargado en una sección
que comienza en la dirección 100. El programa hará
referencias a las direcciones 50,52,54. Pero el contenido de esas
direcciones no es el deseado, sino las direcciones 150, 152 y
154, ya que ahí comienza el programa. La suma de 100 + 50,
...,etcétera se hacen al tiempo de ejecución. La
primera solución vale más la pena que la segunda si
el programa contiene ciclos y es largo, ya que consumirá
menos tiempo en la resolución inicial que la segunda
solución en las resoluciones en línea.
El problema de la protección
Este problema se refiere a que, una vez que un programa ha sido
caragado a memoria en algún segmento en particular, nada
le impide al programador que intente direccionar ( por error o
deliberadamente ) localidades de memoria menores que el
límite inferior de su programa o superiores a la
dirección mayor; es decir, quiere referenciar localidades
fuera de su espacio de direcciones. Obviamente, este es un
problema de protección, ya que no es legal leer o escribir
en áreas de otros programas.
La solución a este problema también puede ser el uso de un registro base y un registro límite. El registro base contiene la dirección del comienzo de la sección que contiene al programa, mientras que el límite contiene la dirección donde termina. Cada vez que el programa hace una referencia a memoria se checa si cae en el rango de los registros y si no es así se envía un mensaje de error y se aborta el programa.
Particiones fijas o particiones variables
En el esquema de la multiprogramación en memroia real se
manejan dos alternativas para asignarle a cada programa su
partición correspondiente: particiones de tamaño
fijo o particiones de tamaño variable. La alternativa
más simple son las particiones fijas. Dichas particiones
se crean cuando se enciende el equipo y permanecerán con
los tamaños iniciales hasta que el equipo se apague. Es
una alternativa muy vieja, quien hacía la división
de particiones era el operador analizando los tamaños
estimados de los trabajos de todo el día. Por ejemplo, si
el sistema tenía 512 kilobytes de RAM, podia asignar 64 k
para el sistema operativo, una partición más de 64
k, otra de 128k y una mayor de 256 k. Esto era muy simple, pero
inflexible, ya que si surgían trabajos urgentes, por
ejemplo, de 400k, tenían que esperar a otro día o
reparticionar, inicializando el equipo desde cero. La otra
alternativa, que surgió después y como necesidad de
mejorar la alternativa anterior, era crear particiones contiguas
de tamaño variable. Para esto, el sistema tenía que
mantener ya una estructura de
datos suficiente para saber en dónde habían
huecos disponibles de RAM y de dónde a dónde
habían particiones ocupadas por programas en
ejecución. Así, cuando un programa requería
ser cargado a RAM, el sistema analizaba los huecos para saber si
había alguno de tamaño suficiente para el programa
que queria entrar, si era así, le asignaba el espacio. Si
no, intentaba relocalizar los programas existentes con el
propósito de hacer contiguo todo el espacio ocupado,
así como todo el espacio libre y así obtener un
hueco de tamaño suficiente. Si aún así el
programa no cabía, entonces lo bloqueaba y tomaba otro. El
proceso con el cual se juntan los huecos o los espacios ocupados
se le llama `compactación'. El lector se habrá dado
cuenta ya de que surgen varios problemas con los esquemas de
particiones fijas y particiones variables:
ø En base a qué criterio se elige el mejor
tamaño de partición para un programa ? Por ejemplo,
si el sistema tiene dos huecos, uno de 18k y otro de 24 k para un
proceso que desea 20 k, ø Cual se le asigna ? Existen
varios algoritmos para darle respuesta a la pregunta anterior,
los cuales se ennumeran y describen enseguida.
Otro problema que se vislumbra desde aquí es que, una vez asignado un hueco, por ejemplo, con "el peor ajuste", puede ser que el proceso requiriera 12 kilobytes y que el hueco asignado fuera de 64 kilobytes, por lo cual el proceso va a desperdiciar una gran cantidad de memoria dentro de su partición, lo cual se le llama `fragmentación interna'.
Por otro lado, conforme el sistema va avanzando en el día, finalizando procesos y comenzando otros, la memoria se va configurando como una secuencia contigua de huecos y de lugares asignados, provocando que existan huecos, por ejemplo, de 12 k, 28k y 30 k, que sumados dan 70k, pero que si en ese momento llega un proceso pidiéndolos, no se le pueden asignar ya que no son localidades contiguas de memoria ( a menos que se realice la compactación ). Al hecho de que aparezcan huecos no contiguos de memoria se le llama `fragmentación externa'.
De cualquier manera, la multiprogramación fue un avance significativo para el mejor aprovechamiento de la unidad central de procesamiento y de la memoria misma, así como dio pie para que surgieran los problemas de asignación de memoria, protección y relocalización, entre otros.
Los overlays
Una vez que surgió la multiprogramación, los
usuarios comenzaron a explorar la forma de ejecutar grandes
cantidades de código en áreas de memoria muy
pequeñas, auxiliados por algunas llamadas al sistema
operativo. Es así como nacen los `overlays'.
Esta técnica consiste en que el programador divide lógicamente un programa muy grande en secciones que puedan almacenarse el las particiones de RAM. Al final de cada sección del programa ( o en otros lugares necesarios ) el programador insertaba una o varias llamadas al sistema con el fin de descargar la sección presente de RAM y cargar otra, que en ese momento residía en disco duro u otro medio de almacenamiento secundario. Aunque esta técnica era eficaz ( porque resolvía el problema ) no era eficiente ( ya que no lo resolvía de la mejor manera ). Esta solución requería que el programador tuviera un conocimiento muy profundo del equipo de cómputo y de las llamadas al sistema operativo. Otra desventaja era la portabilidad de un sistema a otro: las llamadas cambiaban, los tamaños de particiones también. Resumiendo, con esta técnica se podían ejecutar programas más grandes que las particiones de RAM, donde la división del código corría a cuenta del programador y el control a cuenta del sistema operativo.
Multiprogramación en memoria virtual
La necesidad cada vez más imperiosa de ejecutar programas
grandes y el crecimiento en poder de las unidades centrales de
procesamiento empujaron a los diseñadores de los sistemas
operativos a implantar un mecanismo para ejecutar
automáticamente programas más grandes que la
memoria real disponible, esto es, de ofrecer `memoria
virtual'.
La memoria virtual se llama así porque el programador ve una cantidad de memoria mucho mayor que la real, y en realidad se trata de la suma de la memoria de almacenamiento primario y una cantidad determinada de almacenamiento secundario. El sistema operativo, en su módulo de manejo de memoria, se encarga de intercambiar programas enteros, segmentos o páginas entre la memoria real y el medio de almacenamiento secundario. Si lo que se intercambia son procesos enteros, se habla entonces de multiprogramación en memoria real, pero si lo que se intercambian son segmentos o páginas, se puede hablar de multiprogramación con memoria virtual.
La memoria virtual se apoya en varias técnicas interesantes para lograr su objetivo. Una de las teorias más fuertes es la del `conjunto de trabajo', la cual se refiere a que un programa o proceso no está usando todo su espacio de direcciones en todo momento, sino que existen un conjunto de localidades activas que conforman el `conjunto de trabajo'. Si se logra que las páginas o segmentos que contienen al conjunto de trabajo estén siempre en RAM, entonces el programa se desempeñará muy bien.
Otro factor importante es si los programas exhiben un fenómeno llamado `localidad', lo cual quiere decir que algunos programas tienden a usar mucho las instrucciones que están cercanas a la localidad de la instrucción que se está ejecutando actualmente.
Paginación pura
La paginación pura en el majejo de memoria consiste en que
el sistema operativo divide dinámicamente los programas en
unidades de tamaño fijo ( generalmente múltiplos de
1 kilobyte ) los cuales va a manipular de RAM a disco y
viceversa. Al proceso de intercambiar páginas, segmentos o
programas completos entre RAM y disco se le conoce como
`intercambio' o `swapping'. En la paginación, se debe
cuidar el tamño de las páginas, ya que si
éstas son muy pequeñas el control por parte del
sistema operativo para saber cuáles están en RAM y
cuales en disco, sus direcciones reales, etc; crece y provoca
mucha `sobrecarga' (overhead). Por otro lado, si las
páginas son muy grandes, el overhead disminuye pero
entonces puede ocurrir que se desperdicie memoria en procesos
pequeños. Debe haber un equilibrio.
Uno de los aspectos más importantes de la paginación, asi como de cualquier esquema de memoria virtual, es la forma de traducir una dirección virtual a dirección real. Para explicarlo, obsérvese la figura
Como se observa, una dirección virtual `v' = ( b,d) está formada por un número de página virtual `b' y un desplazamiento `d'. Por ejemplo, si el sistema ofrece un espacio de direcciones virtuales de 64 kilobytes, con páginas de 4 kilobytes y la RAM sólo es de 32 kilobytes, entonces tenemos 16 páginas virtuales y 8 reales. La tabla de direcciones virtuales contiene 16 entradas, una por cada página virtual. En cada entrada, o registro de la tabla de direcciones virtuales se almacenan varios datos: si la página está en disco o en memoria, quién es el dueño de la página, si la página ha sido modificada o es de lectura nada mas, etc. Pero el dato que nos interesa ahora es el número de página real que le corresponde a la página virtual. Obviamente, de las 16 virtuales, sólo ocho tendrán un valor de control que dice que la página está cargada en RAM, así como la dirección real de la página, denotada en la figura 4.3 como b' . Por ejemplo, supóngase que para la página virtual número 14 la tabla dice que, efectivamente está cargada y es la página real 2 ( dirección de memoria 8192 ). Una vez encontrada la página real, se le suma el desplazamiento, que es la dirección que deseamos dentro de la página buscada ( b' + d ).
La tabla de direcciones virtuales a veces está ubicada en la misma meoria RAM, por lo cual se necesita saber en qué dirección comienza, en este caso, existe un registro con la dirección base denotada por la letra `a' en la figura 4.3.
Cuando se está buscando una página cualquiera y ésta no está cargada, surge lo que se llama un `fallo de página' (page fault ). Esto es caro para el manejador de memoria, ya que tiene que realizar una serie de pasos extra para poder resolver la dirección deseada y darle su contenido a quien lo pide. Primero, se detecta que la página no está presente y entonces se busca en la tabla la dirección de esta página en disco. Una vez localizada en disco se intenta cargar en alguna página libre de RAM. Si no hay páginas libres se tiene que escoger alguna para enviarla hacia el disco. Una vez escogida y enviada a disco, se marca su valor de control en la tabla de direcciones virtuales para indicar que ya no está en RAM, mientras que la página deseada se carga en RAM y se marca su valor para indicar que ahora ya está en RAM. Todo este procedimiento es caro, ya que se sabe que los accesos a disco duro son del orden de decenas de veces más lentos que en RAM. En el ejemplo anterior se mencionó que cuando se necesita descargar una página de RAM hacia disco se debe de hacer una elección. Para realizar esta elección existen varios algoritmos, los cuales se describen enseguida. _ La primera en entrar, primera en salir: Se escoge la página que haya entrado primero y esté cargada en RAM. Se necesita que en los valores de control se guarde un dato de tiempo. No es eficiente porque no aprovecha ninguna característica de ningún sistema. Es justa e imparcial. _ La no usada recientemente: Se escoge la página que no haya sido usada (referenciada) en el ciclo anterior. Pretende aprovechar el hecho de la localidad en el conjunto de trabajo.
Otro dato interesante de la paginación es que ya no se requiere que los programas estén ubicados en zonas de memoria adyacente, ya que las páginas pueden estar ubicadas en cualquier lugar de la memoria RAM.
Segmentación pura
La segmentación se aprovecha del hecho de que
los programas se dividen en partes lógicas, como son las
partes de datos, de código y de pila (stack). La segmentación asigna particiones de memoria
a cada segmento de un programa y busca como objetivos el
hacer fácil el compartir segmentos ( por ejemplo
librerías compartidas ) y el intercambio entre memoria y
los medios de almacenamiento secundario.
Por ejemplo, en la versión de UNIX SunOS 3.5, no existían librerías compartidas para algunas herramientas, por ejemplo, para los editores de texto orientados al ratón y menús. Cada vez que un usuario invocaba a un editor, se tenía que reservar 1 megabyte de memoria. Como los editores son una herramienta muy solicitada y frecuentemente usada, se dividió en segmentos para le versión 4.x ( que a su vez se dividen en páginas ), pero lo importante es que la mayor parte del editor es común para todos los usuarios, de manera que la primera vez que cualquier usuario lo invocaba, se reservaba un megabyte de memoria como antes, pero para el segundo, tercero y resto de usuarios, cada editor extra sólo consumía 20 kilobytes de memoria. El ahorro es impresionante. Obsérvese que en la segmentación pura las particiones de memoria son de tamaño variable, en contraste con páginas de tamaño fijo en la paginación pura. También se puede decir que la segmentación pura tiene una granularidad menor que la paginación por el tamanó de segmentos versus tamaño de páginas. Nuevamente, para comprender mejor la segmentación, se debe dar un repaso a la forma en que las direcciones virtuales son traducidas a direcciones reales, y para ellos se usa la figura 4.4. Prácticamente la traducción es igual que la llevada a cabo en la paginación pura, tomando en consideracióñ que el tamaño de los bloques a controlar por la tabla de traducción son variables, por lo cual, cada entrada en dicha tabla debe contener la longitud de cada segmento a controlar. Otra vez se cuenta con un registro base que contiene la dirección del comienzo de la tabla de segmentos. La dirección virtual se compone de un número de segmento (s) y un desplazamiento ( d ) para ubicar un byte (o palabra ) dentro de dicho segmento. Es importante que el desplazamiento no sea mayor que el tamaño del segmento, lo cual se controla simplemente checando que ese valor sea mayor que la dirección del inicio del segmento y menor que el inicio sumado al tamaño.
Una ves dada una dirección virtual v=( s,d ), se realiza la operación b + s para hallar el registro (o entrada de la tabla de segmentos ) que contiene la dirección de inicio del segmento en la memoria real, denotado por s'. Ya conociendo la dirección de inicio en memoria real s' sólo resta encontrar el byte o palabra deseada, lo cual se hace sumándole a s' el valor del desplazamiento, de modo que la dirección real ® r = s'+ d.
Cada entrada en la tabla de segmentos tiene un formato similar al mostrado en la figura 4.5. Se tienen campos que indican la longitud, los permisos, la presencia o ausencia y dirección de inicio en memoria real del segmento.
Según amplios experimentos [Deitel93] sugieren que un tamaño de páginas de 1024 bytes generalmente ofrece un desempeño muy aceptable. Intuitivamente parecería que el tener páginas del mayor tamaño posible haría que el desempeño fuera óptimo pero no es así, a menos que la página fuera del tamaño del proceso total. No es así con tamaños grandes de página menores que el proceso, ya que cuando se trae a memoria principal una página por motivo de un solo byte o palabra, se están trayendo muchísimos más bytes de los deseados. La dependencia entre el número de fallas respecto al tamaño de las páginas se muestra en la figura 4.6.
Un hecho notable en los sistemas que manejan paginación es que cuando el proceso comienza a ejecutarse ocurren un gran número de fallos de página, porque es cuando está referenciando muchas direcciones nuevas por vez primera, después el sistema se estabiliza, conforme el número de marcos asignados se acerca al tamaño del conjunto de trabajo.
El la figura 4.7 se muestra la relación entre el tiempo promedio entre fallas de página y el número de marcos de página asignados a un proceso. Allí se ve que el tiempo entre fallas decrece conforme se le asignan más páginas al proceso. La gráfica se curva en un punto, el cual corresponde a que el proceso tiene un número de páginas asignado igual al que necesita para almacenar su conjunto de trabajo. Después de eso, el asignarle a un proceso más páginas que las de su conjunto de trabajo ya no conviene, ya que el tiempo promedio entre fallas permanece sin mucha mejora. Un aspecto curioso de aumentar el número de páginas a un proceso cuando el algoritmo de selección de páginas candidatas a irse a disco es la primera en entrar primera en salir es la llamada `anomalía FIFO' a `anomalía de Belady'. Belady encontró ejemplos en los que un sistema con un número de páginas igual a tres tenía menos fallas de páginas que un sistema con cuatro páginas. El ejemplo descrito en [Tanxx] es injusto. Si se mira con cuidado, obviamente si se compara un sistema con 10 páginas contra otro de 5, ya de inicio el primer sistema tendrá 5 fallos de página más que el de 5, porque se necesitan diez fallos para cargarlo. A esto debería llamársele `anomalía de Belady con corrección.
Sistemas combinados
La paginación y la segmentación puras son
métodos de manejo de memoria bastante efectivos, aunque la
mayoría de los sistemas operativos modernos implantan
esquemas combinados, es decir, combinan la paginación y la
segmentación. La idea de combinar estos esquemas se debe a
que de esta forma se aprovechan los conceptos de la
división lógica
de los programas (segmentos) con la granularidad de las
páginas. De esta forma, un proceso estará repartido
en la memoria real en pequeñas unidades (páginas)
cuya liga son los segmentos. También es factible
así el compartir segmentos a medida que las partes
necesitadas de los mismos se van referenciando (páginas).
Para comprender este esquema, nuevamente se verá
cómo se traduce una dirección virtual en una
localidad de memoria real. Para la paginación y
segmentacíon puras se puede decir que el direccionamiento
es `bidimensional' porque se necesitan dos valores para hallar la
dirección real. Para el caso combinado, se puede decir que
se tiene un direccionamiento `tridimensional'. En la figura 4.8 [
Deitel93] se muestran las partes relevantes para lograr la
traducción de direcciones. El sistema debe contar con una
tabla de procesos (TP). Por cada renglón de esa tabla se
tiene un número de proceso y una dirección a una
tabla de segmentos. Es decir, cada proceso tiene una tabla de
segmentos. Cuando un proceso hace alguna referencia a memoria, se
consulta TP para encontrar la tabla de segmentos de ese proceso.
En cada tabla de segmentos de proceso (TSP) se tienen los
números de los segmentos que componen a ese proceso. Por
cada segmento se tiene una dirección a una tabla de
páginas. Cada tabla de páginas tiene las
direcciones de las páginas que componen a un solo
segmento. Por ejemplo, el segmento `A' puede estar formado por
las páginas reales `a','b','c','p' y `x'. El segmento `B'
puede estar compuesto de las páginas `f','g','j','w' y
`z'.
Para traducir una dirección virtual v=(s,p,d) donde `s' es el segmento, `p' es la página y `d' el desplazamiento en la página se hace lo siguiente. Primero se ubica de qué proceso es el segmento y se localiza la tabla de segmentos de ese proceso en la TP. Con `s' como índice se encuentra un renglón ( registro) en la tabla de segmentos de ese proceso y en ese renglón está la dirección de la tabla de páginas que componen al segmento. Una vez en la tabla de páginas se usa el valor `p' como índice para encontrar la dirección de la página en memoria real. Una vez en esa dirección de memoria real se encuentra el byte (o palabra) requerido por medio del valor de `d'.
Ahora, en este esquema pueden haber dos tipos de fallos: por fallo de página y por fallo de segmento. Cuando se hace referencia a una dirección y el segmento que la contiene no está en RAM ( aunque sea parcialmente ), se provoca un fallo por falta de segmento y lo que se hace es traerlo del medio de almacenamiento secundario y crearle una tabla de páginas. Una vez caragado el segmento se necesita localizar la página correspondiente, pero ésta no existe en RAM, por lo cual se provoca un fallo de página y se carga de disco y finalmente se puede ya traer la dirección deseada por medio del desplazamiento de la dirección virtual.
La eficiencia de la traducción de direcciones tanto en paginación pura, segmentación pura y esquemas combinados se mejora usando memorias asociativas para las tablas de páginas y segmentos, así como memorias cache para guardar los mapeos más solicitados.
Otro aspecto importante es la estrategia para cargar páginas ( o segmentos ) a la memoria RAM. Se usan más comunmente dos estrategias: cargado de páginas por demanda y cargado de páginas anticipada. La estrategia de caragdo por demanda consiste en que las páginas solamente son llevadas a RAM si fueron solicitadas, es decir, si se hizo referencia a una dirección que cae dentro de ellas. La carga anticipada consiste en tratar de adivinar qué páginas serán solicitadas en el futuro inmediato y cargarlas de antemano, para que cuando se pidan ya no ocurran fallos de página. Ese `adivinar' puede ser que se aproveche el fenómeno de localidad y que las páginas que se cargan por anticipado sean aquellas que contienen direcciones contiguas a la dirección que se acaba de refenciar. De hecho, el sistema operativo VMS usa un esquema combinado para cargar páginas: cuando se hace referencia a una dirección cuya página no está en RAM, se provoca un fallo de página y se carga esa página junto con algunas páginas adyacentes. En este caso la página solicitada se cargó por demanda y las adyacentes se cargaron por anticipación.
Uno de los módulos más importantes de un sistema operativo es la de administrar los procesos y tareas del sistema de cómputo. En esta sección se revisarán dos temas que componen o conciernen a este módulo: la planificación del procesador y los problemas de concurrencia.
Planificación del procesador
La planificación del procesador se refiere a la manera o
técnicas que se usan para decidir cuánto tiempo de
ejecución y cuando se le asignan a cada proceso del
sistema. Obviamente, si el sistema es monousuario y monotarea
nohay mucho que decidir, pero en el resto de los sistemas esto es
crucial para el buen funcionamiento del sistema.
Niveles de planificación
En los sistemas de planificación generalmente se
identifican tres niveles: el alto, em medio y el bajo. El nivel
alto decide que trabajos (conjunto de procesos) son candidatos a
convertirse en procesos compitiendo por los recursos del sistema;
el nivel intermedio decide que procesos se suspenden o reanudan
para lograr ciertas metas de rendimiento mientras que el
planificador de bajo nivel es el que decide que proceso, de los
que ya están listos (y que en algún momento paso
por los otros dos planificadores) es al que le toca ahora estar
ejecutándose en la unidad central de procesamiento. En
este trabajo se revisaran principalmente los planificadores de
bajo nivel porque son los que finalmente eligen al proceso en
ejecución.
Objetivos de la planificación
Una estrategia de planificación debe buscar que los
procesos obtengan sus turnos de ejecución apropiadamente,
conjuntamente con un buen rendimiento y minimización de la
sobrecarga (overhead) del planificador mismo. En general, se
buscan cinco objetivos
principales:
Características a considerar de los procesos
Planificación apropiativa o no apropiativa
(preemptive or not preemptive)
La planificación apropiativa es aquella en la cual, una
vez que a un proceso le toca su turno de ejecución ya no
puede ser suspendido, ya no se le puede arrebatar la unidad
central de procesamiento. Este esquema puede ser peligroso, ya
que si el proceso contiene accidental o deliberadamente ciclos
infinitos, el resto de los procesos pueden quedar aplazados
indefinidamente. Una planificación no apropiativa es
aquella en que existe un reloj que lanza interrupciones
periodicas en las cuales el planificador toma el control y se
decide si el mismo proceso seguirá ejecutándose o
se le da su turno a otro proceso. Este mismo reloj puede servir
para lanzar procesos manejados por el reloj del sistema. Por
ejemplo en los sistemas UNIX existen los 'cronjobs' y 'atjobs',
los cuales se programan en base a la hora, minuto, día del
mes, día de la semana y día del año.
En una planificación no apropiativa, un trabajo muy grande aplaza mucho a uno pequeño, y si entra un proceso de alta prioridad esté también debe esperar a que termine el proceso actual en ejecución.
Asignación del turno de ejecución
Los algoritmos de la capa baja para asignar el turno de
ejecución se describen a continuación:
Proceso Espera desde Termina Tiempo de Espera
A 0 4 4
B 0 16 16
C 0 34 34
D 0 58 58
E 0 84 84
Tiempo promedio = (4 + 16 + 34 + 58 + 84 )/5 = 39 unidades.
tiempo que ha esperado + tiempo total para terminar
valor = ___________________________________________
tiempo total para terminar.
Es decir, que dinámicamente el valor se va modificando y
mejora un poco las deficiciencias del algoritmo del trabajo
más corto primero.
Problemas de Concurrencia
En los sistemas de tiempo compartido (aquellos con varios
usuarios, procesos, tareas, trabajos que reparten el uso de CPU
entre estos) se presentan muchos problemas debido a que los
procesos compiten por los recursos del sistema. Imagine que un
proceso está escribiendo en la unidad de cinta y se le
termina su turno de ejecución e inmediatamente
después el proceso elegido para ejecutarse comienza a
escribir sobre la misma cinta. El resultado es una cinta cuyo
contenido es un desastre de datos mezclados. Así como la
cinta, existen una multitud de recursos cuyo acceso debe der
controlado para evitar los problemas de la concurrencia.
El sistema operativo debe ofrecer mecanismos para sincronizar la ejecución de procesos: semáforos, envío de mensajes, 'pipes', etc. Los semáforos son rutinas de software (que en su nivel más interno se auxilian del hardware) para lograr exclusión mutua en el uso de recursos. Para entender este y otros mecanismos es importante entender los problemas generales de concurrencia, los cuales se describen enseguida.
Los problemas descritos son todos importantes para el sistema operativo, ya que debe ser capaz de prevenir o corregirlos. Tal vez el problema más serio que se puede presentar en un ambiente de concurrencia es el 'abrazo mortal', también llamado 'trabazón' y en inglés deadlock. El deadlock es una condición que ningún sistema o conjunto de procesos quisiera exhibir, ya que consiste en que se presentan al mismo tiempo cuatro condiciones necesarias: La condición de no apropiación, la condición de espera circular, la condición de exclusión mutua y la condición de ocupar y esperar un recurso. Ante esto, si el deadlock involucra a todos los procesos del sistema, el sistema ya no podrá hacer algo productivo. Si el deadlock involucra algunos procesos, éstos quedarán congelados para siempre.
En el área de la informática, el problema del deadlock ha provocado y producido una serie de estudios y técnicas muy útiles, ya que éste puede surgir en una sola máquina o como consecuencia de compartir recursos en una red.
En el área de las bases de datos y sistemas distribuidos han surgido técnicas como el 'two phase locking' y el 'two phase commit' que van más allá de este trabajo. Sin embargo, el interés principal sobre este problema se centra en generar técnicas para detectar, prevenir o corregir el deadlock.
Las técnicas para prevenir el deadlock consisten en proveer mecanismos para evitar que se presente una o varias de las cuatro condiciones necesarias del deadlock. Algunas de ellas son:
Para detectar un deadlock, se puede usar el mismo algoritmo
del banquero, que aunque no dice que hay un deadlock, sí
dice cuándo se está en estado inseguro que es la
antesala del deadlock. Sin embargo, para detectar el deadlock se
pueden usar las 'gráficas de recursos'. En ellas se pueden
usar cuadrados para indicar procesos y círculos para los
recursos, y flechas para indicar si un recurso ya está
asignado a un proceso o si un proceso está esperando un
recurso. El deadlock es detectado cuando se puede hacer un viaje
de ida y vuelta desde un proceso o recurso. Por ejemplo, suponga
los siguientes eventos:
evento 1: Proceso A pide recurso 1 y se le asigna.
evento 2: Proceso A termina su time slice.
evento 3: Proceso B pide recurso 2 y se le asigna.
evento 4: Proceso B termina su time slice.
evento 5: Proceso C pide recurso 3 y se le asigna.
evento 6: Proceso C pide recurso 1 y como lo está ocupando
el proceso A, espera.
evento 7: Proceso B pide recurso 3 y se bloquea porque lo ocupa
el proceso C.
evento 8: Proceso A pide recurso 2 y se bloquea porque lo ocupa
el proceso B.
En la figura 5.1 se observa como el 'resource graph' fue evolucionando hasta que se presentó el deadlock, el cual significa que se puede viajar por las flechas desde un proceso o recurso hasta regresar al punto de partida. En el deadlock están involucrados los procesos A,B y C.
Una vez que un deadlock se detecta, es obvio que el sistema está en problemas y lo único que resta por hacer es una de dos cosas: tener algún mecanismo de suspensión o reanudación [Deitel93] que permita copiar todo el contexto de un proceso incluyendo valores de memoria y aspecto de los periféricos que esté usando para reanudarlo otro día, o simplemente eliminar un proceso o arrebatarle el recurso, causando para ese proceso la pérdida de datos y tiempo.
6. Principios en el manejo de entrada – salida
El código destinado a manejar la entrada y salida de los diferentes periféricos en un sistema operativo es de una extensión considerable y sumamente complejo. Resuelve la necesidades de sincronizar, atrapar interrupciones y ofrecer llamadas al sistema para los programadores. En esta sección se repasarán los principios más importantes a tomar en cuenta en este módulo del sistema operativo.
Dispositivos de Entrada - Salida
Los dispositivos de
entrada salida se dividen, en general, en dos tipos:
dispositivos orientados a bloques y dispositivos orientados a
caracteres. Los dispositivos orientados a bloques tienen la
propiedad de
que se pueden direccionar, esto es, el programador puede escribir
o leer cualquier bloque del dispositivo realizando primero una
operación de posicionamiento
sobre el dispositivo. Los dispositivos más comunes
orientados a bloques son los discos duros, la memoria, discos
compactos y, posiblemente, unidades de cinta. Por otro lado, los
dispositivos orientados a caracteres son aquellos que trabajan
con secuencias de byes sin importar su longitud ni ningúna
agrupación en especial. No son dispositivos
direccionables. Ejemplos de estos dispositivos son el teclado, la
pantalla o display y las impresoras.
La clasificación anterior no es perfecta, porque existen varios dispositivos que generan entrada o salida que no pueden englobarse en esas categorías. Por ejemplo, un reloj que genera pulsos. Sin embargo, aunque existan algunos periféricos que no se puedan categorizar, todos están administrados por el sistema operativo por medio de una parte electrónica - mecánica y una parte de software.
Controladores de Dispositivos ( Terminales y Discos Duros)
Los controladores de dispositivos (también llamados
adaptadores de dispositivos) son la parte electrónica de
los periféricos, el cual puede tener la forma de una
tarjeta o un circuito impreso integrado a la tarjeta maestra de
la computadora. Por ejemplo, existen controladores de discos que
se venden por separado y que se insertan en una ranura de la
computadora, o existen fabricantes de computadoras que integran
esa funcionalidad en la misma tarjeta en que viene la unidad
central de procesamiento (tarjeta maestra).
Los controladores de dispositivos generalmente trabajan con voltajes de 5 y 12 volts con el dispositivo propiamente, y con la computadora a través de interrupciones. Estas interrupciones viajan por el 'bus' de la computadora y son recibidos por el CPU el cual a su vez pondrá en ejecución algún programa que sabrá qué hacer con esa señal. A ese programa se le llama 'manejador de disposito' (device driver). Algunas veces el mismo controlador contiene un pequeño programa en una memoria de solo lectura o en memoria de acceso aleatoria no volátil y re-escribible que interactúa con el correspondiente manejador en la computadora. En la figura 6.1 se muestra un esquema simple de dispositivos orientados a bloques y otros a caracteres.
Por ejemplo, la terminal (CRT) tiene un 'chip' que se encarga de enviar cadenas de bits por medio de un cable serial que a su vez son recibidos por un controlador de puerto serial en la computadora. Este 'chip' también se encarga de leer secuencias de bits que agrupa para su despiegue en la pantalla o para ejecutar algunas funciones de control. Lo importante en todos estos dispositivos es que se debe ejercer un mecanismo para sincronizar el envío y llegada de datos de manera concurrente.
Para intercambiar datos o señales entre la computadora y los controladores, muchas veces se usan registros o secciones predefinidas de la memoria de la computadora. A este esquema se le llama 'manejo de entrada - salida mapeado por memoria' (memory mapped I/O). Por ejmplo, para una IBM PC se muestran los vectores de interrupción y las direcciones para la entrada -salida en la tabla 6.1.
Controlador Dirección(Hex) Vector de
Interrupción
Reloj 040 - 043 8
Teclado 060 -
063 9
Disco Duro 320 - 32F 13
Impresora
378 - 37F 15
Monitor Mono 380 - 3BF -
Monitor Color 3D0 -
3DF -
Disco Flexible 3F0 - 3F7 14
Tabla 6.1 Direcciones de Mapeo de Entrada - Salida
Acceso Directo a Memoria (DMA)
El acceso directo a memoria se inventó con el
propósito de liberar al CPU de la carga de atender a
algunos controladores de dispositivos. Para comprender su
funcionamiento vale la pena revisar cómo trabaja un
controlador sin DMA. Cuando un proceso requiere algunos bloques
de un dispositivo, se envia una señal al controlador con
la dirección del bloque deseado. El controlador lo recibe
a través del 'bus' y el proceso puede
estar esperando la respuesta (trabajo síncrono) o puede
estar haciendo otra cosa (trabajo asíncrono). El
controlador recibe la señal y lee la dirección del
bus. Envía a su vez una o varias señales al
dispositivo mecánico (si es que lo hay) y espera los
datos. Cuando los recibe los escribe en un buffer local y
envía una señal al CPU indicándole que los
datos están listos. El CPU recibe esta interrupción
y comienza a leer byte por byte o palabra por palabra los datos
del buffer del controlador (a través del device driver)
hasta terminar la operación.
Como se ve, el CPU gasta varios ciclos en leer los datos deseados. El DMA soluciona ese problema de la manera siguiente. Cuando un proceso requiere uno o varios bloques de datos, el CPU envía al controlador la petición junto con el número de bytes deseados y la dirección de en dónde quiere que se almacenen de regreso. El DMA actuará como un 'cpu secundario' [Stal92] en cuanto a que tiene el poder de tomar el control del 'bus' e indicarle al verdadero CPU que espere. Cuando el controlador tiene listos los datos, el DMA 'escucha' si el 'bus' está libre aprovechando esos ciclos para ir leyendo los datos del buffer del controlador e ir escribiéndolos en el área de memoria que el CPU le indicó. Cuando todos los datos fueron escritos, se le envía una interrupción al CPU para que use los datos. El ahorro con el DMA es que el CPU ya no es interrumpido (aunque sí puede ser retardado por el DMA) salvando así el 'cambio de contexto' y además el DMA aprovechará aquellos ciclos en que el 'bus' no fue usado por el CPU.
El hecho de que los controladores necesiten buffers internos se debe a que conforme ellos reciban datos de los dispositivos que controlan, los deben poder almacenar temporalmente, ya que el CPU no está listo en todo momento para leerlos.
Principios en el Software de Entrada - Salida
Los principios de software en la entrada - salida se resumen en
cuatro puntos: el software debe ofrecer manejadores de
interrupciones, manejadores de dispositivos, software que sea
independiente de los dispositivos y software para usuarios.
Manejadores de interrupciones
El primer objetivo referente a los manejadores de interrupciones
consiste en que el programador o el usuario no debe darse cuenta
de los manejos de bajo nivel para los casos en que el dispositivo
está ocupado y se debe suspender el proceso o sincronizar
algunas tareas. Desde el punto de vista del proceso o usuario, el
sistema simplemente se tardó más o menos en
responder a su petición.
Manejadores de disposisitivos
El sistema debe proveer los manejadores de dispositivos
necesarios para los periféricos, así como ocultar
las peculiaridades del manejo interno de cada uno de ellos, tales
como el formato de la información, los medios
mecánicos, los niveles de voltaje y otros. Por ejemplo, si
el sistema tiene varios tipos diferentes de discos duros, para el
usuario o programador las diferencias técnicas entre ellos
no le deben importar, y los manejadores le deben ofrecer el mismo
conjunto de rutinas para leer y escribir datos.
Software independiente del dispositivo
Este es un nivel superior de independencia
que el ofrecido por los manejadores de dispositivos. Aquí
el sistema operativo debe ser capaz, en lo más posible, de
ofrecer un conjunto de utilerías para accesar
periféricos o programarlos de una manera consistente. Por
ejemplo, que para todos los dispositivos orientados a bloques se
tenga una llamada para decidir si se desea usar 'buffers' o no, o
para posicionarse en ellos.
Software para usuarios
La mayoría de las rutinas de entrada - salida trabajan en
modo privilegiado, o son llamadas al sistema que se ligan a los
programas del usuario formando parte de sus aplicaciones y que no
le dejan ninguna flexibilidad al usuario en cuanto a la
apariencia de los datos. Existen otras librerías en donde
el usuario si tiene poder de decisión (por ejemplo la
llamada a "printf" en el lenguaje
"C"). Otra facilidad ofrecida son las áreas de trabajos
encolados (spooling areas), tales como las de impresión y
correo
electrónico.
Relojes
Los relojes son esenciales para el buen funcionamiento de
cualquier sistema porque juegan un papel decisivo en la
sincronización de procesos, en la calendarización
de trabajos por lote y para la asignación de turnos de
ejecución entre otras tareas relevantes. Generalmente se
cuenta con dos relojes en el sistema: uno que lleva la hora y
fecha del sistema y que oscila entre 50 y 60 veces por segundo y
el reloj que oscila entre 5 y 100 millones de veces por segundo y
que se encarga de enviar interrupciones al CPU de manera
periódica. El reloj de mayor frecuencia sirve para
controlar el tiempo de ejecución de los procesos, para
despertar los procesos que están 'durmiendo' y para lanzar
o iniciar procesos que fueron calendarizados.
Para mantener la hora y fecha del sistema generalmente se usa un registro alimentado por una pila de alta duración que almacena estos datos y que se programan de fábrica por primera vez. Así, aunque se suspenda la energía la fecha permanece. Para lanzar procesos (chequeo de tiempo ocioso de un dispositivo, terminación del time slice de un proceso, etc), se almacena un valor en un registro (valor QUANTUM) el cual se decrementa con cada ciclo del reloj, y cuando llega a cero se dispara un proceso que ejecutará las operaciones necesarias (escoger un nuevo proceso en ejecución, verificar el funcionamiento del motor del disco flexible, hacer eco de un caracter del teclado, etc).
Página siguiente ![]() |
Trabajos relacionados
Ver mas trabajos de Sistemas Operativos |
|
Nota al lector: es posible que esta página no contenga todos los componentes del trabajo original (pies de página, avanzadas formulas matemáticas, esquemas o tablas complejas, etc.). Recuerde que para ver el trabajo en su versión original completa, puede descargarlo desde el menú superior.
Todos los documentos disponibles en este sitio expresan los puntos de vista de sus respectivos autores y no de Monografias.com. El objetivo de Monografias.com es poner el conocimiento a disposición de toda su comunidad. Queda bajo la responsabilidad de cada lector el eventual uso que se le de a esta información. Asimismo, es obligatoria la cita del autor del contenido y de Monografias.com como fuentes de información.
Ingrese el e-mail y contraseña con el que está registrado en Monografias.com
|
|