Con la aparición de computadoras
personales, se crearon los Sistemas Operativos monousuario.
MS-DOS es un claro exponente, ya que sólo puede trabajar
un usuario a la vez. Por ello, este sistema no es más que
una simplificación de los anteriores.
Al desarrollarse las comunicaciones y
fabricarse redes de computadoras, se tuvo que diseñar otro
tipo de Sistema Operativo para gestionar la red. Ahora, debido a
los avances en velocidad y calidad de transmisión de las
telecomunicaciones, el establecimiento de redes de computadoras
privadas, las computadoras multiproceso y al inmenso parque
mundial de computadoras, se está diseñando una
nueva arquitectura de Sistema Operativo. En ella, el mismo
Sistema Operativo se encuentra distribuido por diversas
máquinas para aprovechar al máximo los recursos
globales del sistema.
Concepto de los
Sistemas Operativos
La interfaz entre el sistema operativo y
los programas del usuario se define como el conjunto de
"instrucciones ampliadas" que proporciona el sistema operativo.
Estas instrucciones ampliadas se conocen como llamadas al
sistema. Estas crean, eliminan y utilizan varios objetos del
software, controlados por el sistema operativo.
Los más importantes son los procesos
y archivos.
Procesos
El concepto central de cualquier sistema
operativo es el proceso, una abstracción de un programa en
ejecución.
Todos las computadoras modernas hacen varia
cosas al mismo tiempo. A la vez que ejecuta un programa del
usuario, una computadora puede leer de un disco e imprimir en una
terminal o impresora.
Aunque en sentido estricto, la CPU ejecuta
en cierto instante un solo programa, durante un segundo puede
trabajar con vario de ello, lo que da una apariencia de
paralelismo. A veces, las personas hablan de seudoparalelismo
para indicar este rápido intercambio de los programas en
la CPU, para distinguirlo del paralelismo real del hardware,
donde se hacen cálculos en la CPU a la vez que operan uno
o más dispositivos en E/S. Es difícil mantener un
registro de las distintas actividades paralelas, los
diseñadores de sistema operativos ha desarrollado un
modelo que facilita el uso del paralelismo.
El modelo de proceso
En este modelo, todo el software ejecutable
de la computadora, inclusive el propio sistema operativo, se
organiza en varios procesos secuenciales, o en forma de breves
procesos. Un proceso es tan solo un programa en ejecución,
lo que incluye los valores activos del contador, registro y
variables del programa. De manera conceptual, cada proceso tiene
su propia CPU virtual. Por supuesto, la realidad es que la
verdadera CPU alterna entre los distintos procesos.
La diferencia entre un proceso y un
programa es sutil, pero también crucial. Podemos utilizar
la siguiente analogía para aclarar este punto.
Consideremos un científico de la computación con
una mente culinaria, que esta cocinando la torta de
cumpleaños de su hija. Él tiene una receta para la
torta de cumpleaños, una buena cocina y todo los
ingredientes necesarios (harina, huevo, etc.). En esta
analogía, la receta es el programa, el científico
es el procesador (CPU) y los ingredientes para la torta son los
datos de entrada. El proceso es la actividad en la que el
cocinero lee la receta, busca los ingredientes y cocina el
pastel.
Imaginemos ahora que su hija entra
corriendo, llorando y diciendo que la ha picado una abeja. El
científico registra el punto de la receta donde se
quedó (el estado del proceso activo se resguarda), busca
un libro de primeros auxilios y comienza a seguir las
instrucciones de este. Aquí vemos que el procesador (el
científico) alterna entre un proceso (cocinar) a otro de
mayor prioridad (atender a su hija), cada uno con un programa
distinto (recetario vs. Libro de primero auxilios).
Después de atender la picadura, el científico
regresa al punto donde se encontraba.
La idea clave es que un proceso es una
actividad de cierto tipo. Tiene un programa, entrada, salida y
estado. Un solo procesador puede ser compartido entre varios
procesos.
Jerarquía de procesos
Los sistemas operativos que soportan el
concepto de proceso deben ofrecer cierta forma de crear todos los
procesos necesarios. En los sistemas demasiado sencillos, o en
los sistemas diseñados para la ejecución de un solo
programa, es posible que todos los procesos que podrían
ser necesarios en algún momento pueden estar presentes
durante la inicialización del sistema. Sin embargo, en la
mayoría de los sistemas, es necesaria una forma de crear y
destruir procesos cuando se requiera durante la
operación.
En algunos sistemas operativos los procesos
se crean mediante llamadas a un sistema especial, este crea una
copia idéntica del proceso que hace la llamada y luego el
padre sigue su ejecución en paralelo con su hijo. El padre
puede dar lugar entonces a más hijos. Esta forma de
ejecución se da en sistemas operativos con
multiprogramación (eje. UNIX).
En otros, como el MS-DOS, existe una
llamada al sistema que carga un archivo binario dado en la
memoria y lo ejecuta como un proceso hijo. Pero esta llamada
suspende al padre hasta que el hijo ha finalizado su
ejecución, de forma que el hijo y el padre no se ejecutan
en paralelo.
Estado del proceso
Aunque cada proceso es una entidad
independiente, con su propio contador de programa y estado
interno, es frecuente que los procesos deban intercambiar con
otros.
Existen tres estado del proceso
a.- En ejecución (utiliza la CPU en
el instante dado)
b.- Listo (ejecutable, se detiene en forma
temporal para que se ejecute otro programa)
c.- Bloqueado (no se puede ejecutar debido
a la ocurrencia de algún evento externo)
Desde el punto de vista lógico, los
dos primero estados son similares. En ambos casos, el proceso
desea ejecutarse, sólo que en le segundo caso, no existe
CPU disponible par él. El tercer estado es distinto del
otro puesto que el proceso no se puede ejecutar, incluso aunque
la CPU no tenga labores que realizar.
Son posibles cuatro transiciones entre
estos estados, como la muestra la figura.
La transición 1 ocurre cuando un
proceso descubre que no puede continuar. En ciertos sistemas, el
proceso debe ejecutar una llamada al sistema Block para pasar el
estado de bloque, pero lo más frecuente es que un proceso
lea un archivo especial (por ejemplo una terminal) y no existan
datos disponibles, por lo que el proceso se bloquea en forma
automática.
Las transiciones 2 y 3 se deben al
planificador del proceso (una parte del sistema operativo) sin
que el proceso sepa de ellas. La transición 2 ocurre
cuando el planificador decide que el proceso en ejecución
ya ha sido ejecutado el tiempo suficiente y que es hora de que
otro proceso tenga tiempo en la CPU. La transición 3
ocurre cuando los demás procesos han tenido su parte y es
tiempo de que el primer proceso vuelva a ejecutarse. El tema de
la planificación, es decir, decidir cual proceso debe
ejecutarse, cuando y por cuanto tiempo es muy
importante.
La transición 4 aparece cuando
ocurre el evento externo por el que espera un proceso (como la
llegada de nuevos datos). Si no existe otro proceso en
ejecución es ese momento, se produce la transición
3 en forma inmediata y el proceso comienza su ejecución.
En caso contrario, tendría que esperar en estado Listo por
un momento, hasta que la CPU esté disponible.
Implantación de los
procesos
Para implantar el modelo de proceso, el
sistema operativo utiliza una tabla (un arreglo de estructuras),
llamada la tabla de proceso, con un dato por proceso. Este dato
contiene la información relativa al estado del proceso, el
contador del programa, al apuntador a la pila, asignación
de memoria, el estado de los archivos abiertos, su
información de contabilidad y planificación,
así como todos los datos relativos al proceso que deben
guardarse cuando el proceso alterna entre los estados de
ejecución y listo, de forma que pueda volver a iniciar
más adelante como si nunca se hubiera detenido.
Aunque los campos exactos contenidos en al
tabla de procesos son distintos de sistema en sistema, en general
algunos tiene que ver con la administración del proceso,
con la administración de la memoria y unos mas con el
sistema de archivo como muestra el siguiente cuadro
Es posible explicar la forma en que se
mantiene la ilusión de varios procesos secuenciales en una
maquina con una sola CPU y varios dispositivos de E/S. A cada
clase de dispositivos de E/S (ej. Discos flexibles, discos duros,
etc.) se le asocia una localidad cerca de la parte inferior de la
memoria, llamada vector de interrupción, que contiene la
dirección del procedimiento de servicio a las
interrupciones. Supongamos que el proceso del usuario 3
está en ejecución cuando ocurre una
interrupción del disco. El contador del programa, la
palabra de estado del programa y tal vez uno o más
registros son enviados a la pila por el hardware de la
interrupción. Es entonces cuando la computadora pasa a la
dirección dada por el vector de interrupción de
disco. Esto es todo lo que hacer el hardware. De aquí en
adelante, todo queda en manos del software.
El procedimiento de servicio a la
interrupción comienza guardando todos los registros en los
datos de la tabla de procesos correspondientes al proceso activo.
El número de procesos activo y un apuntador a este dato se
mantienen como variables globales para una rápida
localización. Después la información
depositada por la interrupción se elimina de la pila. Las
acciones tales como guardar los registro y establecer el
apuntador a la pila se pueden expresar en C (o cualquier otro
lenguaje de alto nivel) de esta forma pueden llevarse a cabo
mediante pequeñas rutinas en lenguaje ensamblador. Al
terminar esta rutina se llama un procedimiento en C que lleva a
cabo el trabajo real de la interrupción.
El siguiente paso consiste en determinar el
proceso que inicio la solicitud al disco. Este se habrá
ido a dormir luego de iniciar la solicitud, por lo que
deberá ser despertado. El estado de este proceso cambia de
bloqueado a listo y luego se llama al planificador.
Sabemos que al menos dos procesos
están listos: el proceso que inicio la E/S del disco y
aquél que fue interrumpido. La elección de quien se
ejecutara dependerá del algoritmo del
planificador.
Archivos
Como ya mencionamos anteriormente, una de
las funciones principales del sistema operativo es la de ocultar
las peculiaridades de los discos y demás dispositivos de
entrada/salida, para presentar al programador un modelo agradable
y nítido de archivos independientes de los
dispositivos.
Para poder proporcionar un espacio donde
almacenar los archivos, los sistemas operativos soportan el
concepto de directorio como una forma de agrupar los
archivos.
Es evidente la necesidad de llamadas al
sistema en la creación, eliminación, lectura y
escritura de archivos. Antes de poder leer un archivo, hay que
abrir este, después de leer un archivo, este debe
cerrarse; siendo las llamadas al sistema, las que permiten
realizar estas operaciones.
Administración 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 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
podía tomar otro proceso para que usara la unidad central
de procesamiento. De esta forma, la memoria física 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.6×0.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 día 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 cargado 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 sé
checa si cae en el rango de los registros y si 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 memoria 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, podía 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ía 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 quería 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 enumeran y describen
enseguida.
o Primer Ajuste: Se asigna el primer hueco
que sea mayor al tamaño deseado.
o Mejor Ajuste: Se asigna el hueco cuyo
tamaño exceda en la menor cantidad al tamaño
deseado. Requiere de una búsqueda exhaustiva.
o Peor Ajuste: Se asigna el hueco cuyo
tamaño exceda en la mayor cantidad al tamaño
deseado. Requiere también de una búsqueda
exhaustiva.
o El Siguiente Ajuste: Es igual que el
`primer ajuste' con la diferencia que se deja un apuntador al
lugar en donde se asignó el último hueco para
realizar la siguiente búsqueda a partir de
él.
o Ajuste Rápido: Se mantienen listas
ligadas separadas de acuerdo a los tamaños de los huecos,
para así buscarle a los procesos un hueco más
rápido en la cola correspondiente.
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, a 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 en 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
teorías 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 manejo 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 tamañ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, así 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 4.3.
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 memoria 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.
• La usada menos recientemente: Es
parecida a la anterior, pero escoge la página que se
usó hace más tiempo, pretendiendo que como ya tiene
mucho sin usarse es muy probable que siga sin usarse en los
próximos ciclos. Necesita de una búsqueda
exhaustiva.
• La no usada frecuentemente: Este
algoritmo toma en cuenta no tanto el tiempo, sino el
número de referencias. En este caso cualquier
página que se use muy poco, menos veces que alguna
otra.
• La menos frecuentemente usada: Es
parecida a la anterior, pero aquí se busca en forma
exhaustiva aquella página que se ha usado menos que todas
las demás.
• En forma aleatoria: Elige cualquier
página sin aprovechar nada. Es justa e imparcial, pero
ineficiente.
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 tamaño 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ón 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
chequeando 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 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 mustra 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.
En la figura 4.7 se muestra la
relación entre el tiempo promedio, las 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, él asignarle a
un proceso más páginas que las de su conjunto de
trabajo ya no convienen, 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 es injusto. 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
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 cargado 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 comúnmente dos estrategias: cargado de
páginas por demanda y cargado de páginas
anticipada. La estrategia de cargado 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.
MS-DOS
Como ejemplo de un sistema operativo de un
solo procesador, nos referiremos a MS-DOS, que sólo se
ejecuta en el Intel 8088 y sus sucesores, 286, 386 y
486.
Iniciaremos con su historia, analizaremos
los conceptos fundamentales, algunas de sus llamadas al sistema
y, por último, diremos algo de su
implantación.
Historia De Ms-Dos
La primera computadora personal fue la
Altair, producida en 1975 por la compañía MITS.
Tenía un CPU con el Intel 8080 de 8 bits y 256 bytes de
memoria. No tenía teclado, pantalla, cintas o
discos.
Después de unos años, muchas
compañías comenzaron a fabricar computadoras
personales con base en el chip 8080, casi todas con un sistema
operativo llamado CP/M.
La PC-IBM
Alrededor de 1980, IBM, vio la necesidad de
tener su propia computadora personal. Para entonces Intel
había producido dos sucesores del 8080, el 8086 de 16 bits
y una versión de él con un bus de 8 bits, el 8088
que fue comprada por IBM. Microsoft compró para IBM un
sistema operativo del tipo de CP/M-86 al cual le realizo unos
cuantos arreglos. Cambiaron su nombre por el de MS-DOS (Micro
Soft – Sistema operativo de disco). IBM anunció la
PC en agosto de 1981.
MS-DOS podía ejecutar la mayor parte
del software que se utilizaba entonces en el 8080 bajo
CP/M.
La PC estaba equipada con hardware para el
control de las cintas de audiocassette y palancas de control para
los videojuegos.
Respecto del hardware de la PC. Aunque el
8088 tiene un espacio de direcciones de 1 megabyte, IBM
decidió asignar los primeros 640 de éstos al RAM y
el resto a ROM, tarjetas de vídeo y otros elementos. En
consecuencia, la configuración de MS-DOS sólo
soporta programas con un tamaño máximo de 640 K. Al
surgir después modelos con hasta 16 MB, la incapacidad
para ejecutar programas mayores de 640 K se convirtió en
un problema.
Los programas de la PC de IBM eran libres
de no utilizar el sistema operativo y tener acceso directo al
hardware.
MS-DOS Versión 1.0
Fue lanzada junto con la PC DE IBM. Ocupaba
12 K de los 64 K de la memoria de la máquina. El
código constaba de 4000 líneas de código
ensamblador. El único disco que soportaba era el de 5
¼ plg, de un solo lado y 160 K.
El sistema operativo constaba de 3
programas: ibmbio.com, el sistema de E/S de discos y caracteres;
ibmdos.com, el manejador de discos y archivos; y command.com, el
procesador de comandos, un shell primitivo. MS-DOS siempre ha
hecho uso de un ROM en hardware integrado a la PC DE IBM, llamado
BIOS (Sis. Básico de E/S). BIOS contiene los manejadores
de los dispositivos estándar, por lo que bastaba que
MS-DOS los llamara para realizar la E/S.
El BIOS se localizaba cerca de la parte
superior del espacio de direcciones de MI en el ROM del 8088, no
ocupaba RAM.
La versión MS-DOS 1.0 era compatible
con CP/M.
Microsoft lanzó la versión
1.1 en 1982, la cual soportaba los discos de 320K de doble lado.
En lo demás era similar a la versión
1.0.
MS-DOS Versión 2.0
En 1983, IBM presentó la PC/XT, su
primera computadora personal con un disco duro. Venía con
la versión 2.0. Aunque soportaba las llamadas al sistema
de CP/M, Microsoft volvió a escribirlo desde cero y le
incorporó ideas de UNIX como las llamadas al sistema OPEN,
READ, WRITE y CLOSE. El shell también se mejoró;
podía manejar el redireccionamiento de la E/ S,
además de soportar entubamientos y filtros.
MS-DOS 2.0 también incluyó
manejadores de dispositivos que podía instalar el usuario,
cola de impresión (spooloing), configuración del
sistema, administración de la memoria y shells
adaptados.
Al surgir una gran demanda a nivel mundial
de la PC/XT produjo la versión 2.05, que soportaba la
hora, fecha, moneda y símbolos decimales de muchos
países.
MS-DOS Versión 3.0
En 1984, IBM lanzó la PC/AT, su
primera computadora personal con base en el chip 286. Esta
soportaba una memoria de hasta 16 MB, tenía modos usuario
y núcleo, un modo de protección con base en anillos
y capacidad de ejecutar varios programas a la vez. La
versión de MS/DOS empacada con la PC/AT era la
3.0.
La PC/AT venía con una unidad de
disco de 1.2 M, reloj con batería y la información
de la configuración en CMOS, se añadió el
soporte para estos dispositivos.
Además, ahora se soportaban los
discos duros de más de 10M y se presentaron los discos en
RAM y el procesador de comandos (shell) se eliminó del
sistema operativo y se conformó como un programa
independiente.
El 3.0 se reemplazó por 3.1, que
proporcionó el primer soporte para las redes.
La siguiente edición fue la 3.2.
Soportaba discos de 3 ½ plg..
En 1987, IBM presentó el sucesor de
la línea PC, la familia PS/2 (Sistema Personal 2).
Venían con discos de 3 ½ plg. y 720K en las
versiones más pequeñas y discos de 3 ½ plg.
con 1.44M en las versiones más grandes.
IBM y Microsoft lanzaron un sistema
operativo nuevo llamado OS/2. Como se entregó tarde e
incompleto, Microsoft lo desechó, esto molestó a
IBM, que abandonó a Microsoft y firmó contrato con
Apple Computer para su futuro software.
MS-DOS Versión 4.0
IBM presentó la versión 4.0.
Una de las grandes mejoras de esta edición fue el soporte
de discos duros mayores de 32 M. MS-DOS 4.0 soporta discos de
hasta 2 gigabytes. Aunque los programas seguían
restringidos a 640 K, se podían utilizar hasta 16M de
memoria extendida para el disco en RAM. Otra mejora fue el shell
de DOS, controlado mediante menús. En fin, esta
versión no se utilizó con amplitud.
MS-DOS Versión 5.0
Fue anunciada en 1991, fue la primera
versión que hacía un uso serio de la memoria
extendida, de la que muchos poseedores de 286 y 386 tenían
varios megabytes. Aún con la restricción de que los
programas no podían exceder los 640K, al menos
tenía la capacidad de colocar la mayoría del propio
MS-DOS en la memoria extendida, de modo que cerca de 600K de los
640K inferiores estaban disponibles para los programas del
usuario. Además, los manejadores de dispositivos escritos
por el usuario también se podrían colocar en la
memoria extendida. MS-DOS 5.0 también podía
utilizar la memoria entre los 640K y 1M de las máquinas
386 para los manejadores de dispositivos y ciertas
utilerias.
También proporcionó un nuevo
shell, con la capacidad de tener varios programas en la memoria
al mismo tiempo. 5.0 también venía con un amplio
programa de ayuda (HELP), para auxiliar a los nuevos
usuarios.
Panorama de
MS-DOS
Uso de MS-DOS
Existe un shell, comand.com, un sistema de
archivos, llamadas al sistema, programas de utilerías y
otras características.
Para utilizar MS-DOS sólo hay que
encender la computadora. Unos segundos después, aparece el
indicador del shell.
Por la razón de que la
máquina es utilizada por una única persona, los
archivos y directorios no tienen propietarios y no existen bits
de protección.
Para ejecutar un programa, se escriben su
nombre y argumentos en el shell.
Los comandos de MS-DOS se dividen en dos
categorías: internos y externos. Los internos se ejecutan
por el propio shell, los externos son auténticos
programas, que, por lo general, se encuentran en el directorio
/dos o /bin.
En MS-DOS, muchas de las utilerías
de más uso son comandos internos del shell. En total
existen 40 comandos internos.
La construcción de tantos comandos
dentro del shell representa una situación contradictoria.
Por un lado, el hecho de no tener que buscarlos en un disco lento
los hace más rápidos. Puesto que la PC DE IBM
original no tenía un disco duro. Por otro lado, todo ese
código hace que el shell sea más grande.
En MS-DOS, la línea de comandos se
transfiere de manera literal al programa.
MS-DOS 5.0 tiene un programa doskey que se
puede instalar con el fin de registrar y guardar en un buffer
todas las combinaciones de teclas. Su función es permitir
la repetición de comandos, con o sin edición. Por
ejemplo, al oprimir la tecla F7, doskey exhibe una lista de los
comandos más recientes. Se puede seleccionar uno de ellos
mediante la tecla F9. El comando se puede volver a ejecutar de
manera directa, o bien editarlo y volverlo a ejecutar.
El Shell de MS-DOS
MS-DOS tiene ahora una interfaz orientada a
la pantalla, llamada dosshell. Despliega los archivos y
directorios en ventanas y permite que los usuarios realicen una
gran cantidad de trabajo al apuntar mediante el ratón y
oprimir el botón de éste.
Al iniciar dosshell, se exhiben varias
ventanas en la pantalla. Pueden aparecer varias pantallas, una de
las cuales se muestra en la figura. Esta contiene 8
ventanas.
La ventana superior es la barra de
título, la cual identifica el programa en
ejecución. A continuación aparece la barra de
menú, la cual ofrece varias opciones.
o Al oprimir File aparece un menú
que permite a los usuarios desplazar, copiar, cambiar el nombre e
imprimir archivos, crear directorios, así como otras
operaciones análogas. La mayoría de éstas
utilizan el ratón y el teclado.
o El menú Options activa o desactiva
varias opciones. También permite al usuario seleccionar
los colores de la pantalla.
o El menú View controla los cuatro
tipos de ventanas que se pueden exhibir y el tamaño de
cada una.
o El menú Tree controla las opciones
de la forma en que se exhiben los subdirectorios en la
pantalla
o El menú Help proporciona ayuda en
relación con varios aspectos de dosshell.
o La siguiente sección de la
pantalla contiene los iconos de unidad. En MS-DOS cada unidad de
disco tiene asignado un código. Los dos primeros discos se
llaman A: y B: y el primer disco duro se llama C:.
o Por último están las cuatro
ventanas más importantes.
o La ventana Directory Tree exhibe el
directorio raíz de la unidad C. El símbolo + indica
que el directorio tiene subdirectorios que no se muestran. Al
oprimir el botón sobre +, éste símbolo
cambia por – y se muestran los subdirectorios. Al
seleccionar una entrada sus archivos aparecen en la ventana File
List
o La ventana Main tiene una lista de los
comandos de uso frecuente. Los programas se ejecutan al oprimir
el botón del ratón sobre ellos.
o La última ventana es, la
más interesante. En este momento, el usuario es libre de
ejecutar los comandos que desee, como por ejemplo, iniciar otro
programa, el cual se puede suspender. Todos los programas
suspendidos aparecen en esta ventana. Si se oprime el
botón del ratón dos veces sobre alguno de ellos,
este programa continúa su ejecución.
o Por último, la última
ventana es la Status. Tiene una lista de ciertas abreviaturas de
comandos, exhibe mensajes y muestra la hora del
día.
Configuración de MS-DOS
MS-DOS se puede configurar en una infinidad
de formas. Por ejemplo, los usuarios son libres de instalar sus
propios manejadores de interruptores.
Al registrar las interrupciones del
teclado, el programa doskey puede examinar todos los caracteres
tecleados antes de que éstos pasen al sistema
operativo.
Otra forma de configuración por
parte del usuario es la capacidad de instalar manejadores de
dispositivos adaptados a las necesidades de aquéllos.
Estos manejadores pueden controlar a los dispositivos de E/S no
estándar, como los sintetizados de música MIDI.
También pueden manejar los dispositivos estándar,
como la memoria extendida, de forma no estándar (por
ejemplo, simular discos en RAM).
MS-DOS tiene la capacidad de asociar los
códigos de teclas producidos por el teclado con diferentes
caracteres, por ej. la tecla a la derecha de TAB es una Q (EEUU)
o una A (Francia).
Gran parte de la capacidad de
configuración de MS-DOS se controla mediante un archivo
llamado config.sys, el cual se lee al arrancar la máquina.
Puede contener comandos para la instalación de manejadores
de dispositivos adecuados, establecer el soporte des lenguaje
nacional, determinar la parte de la memoria donde debe colocarse
el sistema operativo, asignar memoria para el buffer
caché, especificar el número máximo de
archivos abiertos y seleccionar el shell. Además,
después de procesar este archivo, se ejecuta un archivo
por lotes llamado autoexec.bat.
Conceptos Fundamentales en
MS-DOS
En MS-DOS, el modelo de un proceso y su uso
de memoria están fuertemente entrelazados e
íntimamente relacionados con los detalles de la
arquitectura del CPU 8088 y la arquitectura del sistema PC DE
IBM. Además, la programación del modelo de los
procesos y de la memoria son inseparables de la
implantación.
Procesos en MS-DOS
MS-DOS no es un sistema de
multiprogramación, tampoco es un sistema de
monoprogramación. Al arrancar el sistema, un proceso,
command.com (el shell orientado al teclado) se inicia y espera
una entrada. Al escribir una línea, command.com inicia un
nuevo proceso, le transfiere el control y espera a que
éste termine.
En MS-DOS, el padre y el hijo no pueden
ejecutarse en paralelo. Cuando un proceso produce un hijo, el
padre se suspende de manera automática hasta que el hijo
hace una salida. El resto se suspende en espera de que un hijo
termine. Aunque MS-DOS permite la existencia de varios procesos a
la vez, no es un verdadero sistema de
multiprogramación.
MS-DOS tiene dos tipos de archivos binarios
ejecutables, los que producen dos tipos distintos de procesos. Un
archivo con extensión COM, como PROG.COM, es un simple
archivo ejecutable, están almacenados en archivos que
contienen una copia fiel del código a ser ejecutado. El
archivo se carga en la memoria tal cual es y se
ejecuta.
El otro tipo de archivo ejecutable es el
archivo EXE. Un proceso creado a partir de estos archivos puede
tener un segmento de texto, un segmento de datos, un segmento de
pila, etc.
Estos pueden ser reasignados conforme se
cargan en la memoria. El sistema operativo indica la diferencia
entre los archivo COM y los archivos EXE mediante los dos
primeros bytes y no por medio de la extensión del nombre
del archivo.
Los primeros 256 bytes de cada proceso de
MS-DOS forman un bloque especial de datos llamado PSP (prefijo
del segmento del programa). El sistema operativo construye este
bloque al momento de crear el proceso. Para los archivos.com.,
cuenta como parte del espacio de direcciones del proceso y se
puede hacer referencia a él mediante las direcciones 0 a
255. Por esta razón todos los procesos. Com. comienzan en
la dirección 256 y no en la dirección 0. Por el
contrario, los archivos.Exe se reasignan por arriba de PSP, por
lo que su dirección 0 es el primer byte por arriba del
PSP. Esto evita desperdiciar 256 bytes de espacio de
direcciones.
PSP contiene el tamaño del programa,
un apuntador al bloque del ambiente, la dirección del
manejador CTRL-C, la cadena del comando, un apuntador al PSP del
padre, la tabla de descriptores del archivo y otra
información.
En MS-DOS un hijo hereda, por lo general,
los archivos abiertos del padre. Si un hijo lee o escribe en un
archivo abierto, al concluir, el padre ve la nueva
posición del archivo y puede transferirla al siguiente.
Los archivos abiertos por el propio hijo se cierran de manera
automática al terminar éste y su memoria es
liberada.
Por lo general, cuando un proceso termina
se reclama su memoria y el proceso desaparece para siempre. Sin
embargo, MS-DOS tiene una alternativa, donde se indica al sistema
que no recupere su memoria, sino que considere que hizo su
salida. Esta característica ha generado la
fabricación de software TSR (terminar y permanecer
residente o software residente en memoria). Son aquellos
programas que permanecen en memoria aunque no estén siendo
ejecutados. Su presencia en memoria es latente, están
listos para activarse en el momento que sean requeridos o sucedan
en la máquina las condiciones que están
esperando.
Los procesos en MS-DOS pueden instalar sus
propios manejadores de interrupciones.
El programa TSR no puede utilizar MS-DOS
para leer nuevos datos mediante el teclado y no lo puede utilizar
para escribir en la pantalla. Tampoco lo puede utilizar para
tener acceso a archivos. Ni siquiera puede utilizar el BIOS,
puesto que éste también podría estar a mitad
de una llamada al momento de oprimir la tecla adecuada.
Así, el programa TSR debe realizar su propia
E/S.
El modelo de memoria de MS-DO
Es bastante complejo. El espacio de
direcciones se divide en cuatro regiones, con tamaños y
propiedades diferentes. Así, para comprender el modelo de
memoria de MS-DOS, es esencial comprender la arquitectura de la
PC DE IBM y sus sucesoras.
La arquitectura de memoria del
8088
El 8080 es un CPU de 8 bits de Intel.
Tenía varios registros de 8 bits, entre los que se
encontraba un acumulador de 8 bits y 2 registros de direcciones
de 8 bits, H y L, los cuales también podían
utilizarse como registros de 16 bits.
Aunque el 8080 sólo tenía una
aritmética de 8 bits, tenía un direccionamiento
real de 16 bits y de hasta 64K bytes de memoria, donde cada byte
se podía direccionar de manera independiente.
Los sucesores del 8080, el 8086 y el 8088,
se diseñaron para ser mejor que el 8080, pero que
también fueran compatibles.
El 8088 tiene 12 registros:
a. Cuatro registros de 16 bits, denominados
AX, BX, CX Y DX, que pueden ser direccionados de 8 registros de 8
bits, denominados AH,AL,BH,BL,CH,CL,DH,DL. El registro AX sirve
fundamentalmente como acumulador y como registro de transferencia
en las instrucciones E/S. El registro BX puede usarse como
acumulador y como registro base para calcular las direcciones de
los datos de memoria. El registro CX puede usarse como acumulador
y se utiliza como contador para las instrucciones interactivas.
El registro DX puede usarse como acumulador y se emplea como
puntero de datos en ciertas instrucciones especificas de
E/S.
b. Cuatro registros de puntero de segmento
denominados CS, DS, SS y ES. Dicho puntero define cuatro
segmentos de 64K bytes cada uno. Cualquier dirección de
memoria se forma, sumando al puntero del segmento una
dirección efectiva calculada por diversos procedimientos.
El registro CS, (segmento de código) se utiliza para
reasignar el contador del programa; el registro SS (segmento de
pila). Este segmento lo utilizan principalmente los programadores
en lenguaje ensamblador, se utiliza para reasignar segmentos a la
pila; el registro DS (segmento de dato) se usa en instrucciones
que manejan datos de memoria y el registro ES (segmento extra) se
utiliza en instrucciones que manejan cadena de
caracteres.
c. Cuatro registros que contienen
direcciones de desplazamiento dentro de los segmentos denominados
SP,BP,SI,DI. El registro SP puntero de la pila de los registros
SI y DI son registros de índice, contienen desplazamiento
de los punteros de segmento DS y ES en las instrucciones que
manejan cadena de caracteres. El registro BP es el puntero base
que se utiliza para apuntar a la base del marco de pila activo
(para el acceso a variables locales). Todos estos registros deben
contener apuntadores.
El 8086 representa la arquitectura base
para todos los microprocesadores de 16 bits de Intel: 8088, 8086,
80188, 80186 y 80286. Toda la familia 80×86 en adelante posee dos
características en común:
a. Arquitectura segmentada. Esto significa
que la memoria es dividida en segmentos con un tamaño
máximo de 64K (información importante para el
direccionamiento de la memoria en la futura programación
segmentada en el lenguaje ensamblador)
b. Compatibilidad de las instrucciones y
registros de las anteriores versiones son soportados por las
nueve versiones, y estas versiones son soportadas por versiones
anteriores.
Autor:
Juan Turizo Ospino
Página anterior | Volver al principio del trabajo | Página siguiente |