viernes, 16 de junio de 2017

Software de Salida

Ahora consideremos el software de salida. Primero analizaremos una salida de ejemplo a una ventana de texto, que es lo que los programadores prefieren utilizar comúnmente. Después consideraremos las interfaces gráficas de usuario, que otros usuarios prefieren a menudo.
Ventanas de texto
La salida es más simple que la entrada cuando se envía secuencial mente en un solo tipo de letra, tamaño y color. En su mayor parte, el programa envía caracteres a la ventana en uso y se muestran ahí. Por lo general, un bloque de caracteres (por ejemplo, una línea) se escribe en una llamada al sistema.

Software de Entrada

La entrada del usuario proviene principalmente del teclado y del ratón; analicemos estos dispositivos. En una computadora personal, el teclado contiene un microprocesador integrado que por lo general se comunica, a través de un puerto serial especializado, con un chip controlador en la tarjeta principal (aunque cada vez con más frecuencia, los teclados se conectan a un puerto USB).
 Se genera una interrupción cada vez que se oprime una tecla, y se genera una segunda interrupción cada vez que se suelta. En cada una de estas interrupciones de teclado, el software controlador del mismo extrae la información acerca de lo que ocurre desde el puerto de E/S asociado con el teclado. Todo lo demás ocurre en el software y es muy independiente del hardware. La mayoría del resto de esta sección se puede comprender mejor si se considera que se escriben comandos en una ventana de shell (interfaz de línea de comandos). Así es como trabajan comúnmente los programadores. A continuación analizaremos las interfaces gráficas.


Software de Reloj

Todo lo que hace el hardware del reloj es generar interrupciones a intervalos conocidos. Todo lo demás que se relacione con el tiempo debe ser realizado por el software controlador del reloj. Las tareas exactas del controlador del reloj varían de un sistema operativo a otro, pero por lo general incluyen la mayoría de las siguientes tareas:
1. Mantener la hora del día.
2. Evitar que los procesos se ejecuten por más tiempo del que tienen permitido.
3. Contabilizar el uso de la CPU.
4. Manejar la llamada al sistema alarm que realizan los procesos de usuario.
5. Proveer temporizadores guardianes (watchdogs) para ciertas partes del mismo sistema.
6. Realizar perfilamiento, supervisión y recopilación de estadísticas.
La primera función del reloj, mantener la hora del día (también conocida como tiempo real), no es difícil. Sólo requiere incrementar un contador en cada pulso de reloj, como se dijo antes. Lo único por lo que debemos estar al pendiente es el número de bits en el contador de la hora del día. Con una velocidad de reloj de 60 Hz, un contador de 32 bits se desbordará aproximadamente en 2 años. Es evidente que el sistema no puede almacenar el tiempo real como el número de pulsos del reloj desde enero 1, 1970 en 32 bits. Se pueden usar tres métodos para resolver este problema. El primero es utilizar un contador de 64 bits, aunque para ello se vuelve más complejo dar mantenimiento al contador, ya que se tiene que realizar muchas veces por segundo. El segundo es mantener la hora del día en segundos, en vez de pulsos, usando un contador subsidiario para contar pulsos hasta que se haya acumulado un segundo completo. Como 232 segundos equivale a más de 136 años, este método funcionará hasta el siglo XXII. El tercer método es contar en pulsos, pero hacerlo de manera relativa a la hora en que se inició el sistema, en vez de un momento externo fijo. Cuando el reloj de respaldo se lee o el usuario escribe la hora real, el tiempo de inicio del sistema se calcula a partir del valor de la hora del día actual y se almacena en memoria, en cualquier forma conveniente. Más adelante, cuando se solicita la hora del día, se suma al contador la hora almacenada del día para obtener la hora actual. Los tres métodos se muestran en la figura 5-33. La segunda función del reloj es evitar que los procesos se ejecuten en demasiado tiempo. Cada vez que se inicia un proceso, el planificador inicializa un contador para el valor del quantum de ese proceso, en pulsos de reloj. En cada interrupción del reloj, el software controlador del mismo decrementa el contador del quantum en 1. Cuando llega a cero, el software controlador del reloj llama al planificador para establecer otro proceso.


Hardware de Reloj

Hay dos tipos de relojes de uso común en las computadoras, y ambos son bastante distintos de los relojes que utilizan las personas. Los relojes más simples están enlazados a la línea de energía de 110 o 220 voltios y producen una interrupción en cada ciclo de voltaje, a 50 o 60 Hz. Estos relojes solían dominar el mercado, pero ahora son raros. El otro tipo de reloj se construye a partir de tres componentes: un oscilador de cristal, un contador y un registro contenedor, como se muestra en la figura 5-32. Cuando una pieza de cristal de cuarzo se corta en forma apropiada y se monta bajo tensión, puede generar una señal periódica con una precisión muy grande, por lo general en el rango de varios cientos de megahertz, dependiendo del cristal elegido. Mediante el uso de componentes electrónicos, esta señal base puede multiplicarse por un pequeño entero para obtener frecuencias de hasta 1000 MHz o incluso más. Por lo menos uno de esos circuitos se encuentra comúnmente en cualquier computadora, el cual proporciona una señal de sincronización para los diversos circuitos de la misma. Esta señal se alimenta al contador para hacer que cuente en forma descendente hasta cero. Cuando el contador llega a cero, produce una interrupción de la CPU. 
Oscilador 


Para evitar que se pierda la hora actual cuando se apaga la computadora, la mayoría cuentan con un reloj de respaldo energizado por batería, implementado con el tipo de circuitos de baja energía que se utilizan en los relojes digitales. El reloj de batería puede leerse al iniciar el sistema. Si no está presente, el software puede pedir al usuario la fecha y hora actuales. También hay una forma estándar para que un sistema obtenga la hora actual de un host remoto. En cualquier caso, la hora se traduce en el número de pulsos de reloj desde las 12 A.M. UTC (Universal Coordinated Time, Tiempo coordenado universal) (anteriormente conocido como Tiempo del meridiano de Greenwich) el 1 de enero de 1970, como lo hace UNIX, o a partir de algún otro momento de referencia. El origen del tiempo para Windows es enero 1, 1980. En cada pulso de reloj, el tiempo real se incrementa por un conteo. Por lo general se proporcionan programas utilitarios para 
establecer el reloj del sistema y el reloj de respaldo en forma manual, y para sincronizar los dos relojes.


Hardware de Reloj

Hay dos tipos de relojes de uso común en las computadoras, y ambos son bastante distintos de los relojes que utilizan las personas. Los relojes más simples están enlazados a la línea de energía de 110 o 220 voltios y producen una interrupción en cada ciclo de voltaje, a 50 o 60 Hz. Estos relojes solían dominar el mercado, pero ahora son raros. El otro tipo de reloj se construye a partir de tres componentes: un oscilador de cristal, un contador y un registro contenedor, como se muestra en la figura 5-32. Cuando una pieza de cristal de cuarzo se corta en forma apropiada y se monta bajo tensión, puede generar una señal periódica con una precisión muy grande, por lo general en el rango de varios cientos de megahertz, dependiendo del cristal elegido. Mediante el uso de componentes electrónicos, esta señal base puede multiplicarse por un pequeño entero para obtener frecuencias de hasta 1000 MHz o incluso más. Por lo menos uno de esos circuitos se encuentra comúnmente en cualquier computadora, el cual proporciona una señal de sincronización para los diversos circuitos de la misma. Esta señal se alimenta al contador para hacer que cuente en forma descendente hasta cero. Cuando el contador llega a cero, produce una interrupción de la CPU. 

Formato de Discos

Un disco duro consiste en una pila de platos de aluminio, aleación de acero o vidrio, de 5.25 o 3.5 pulgadas de diámetro (o incluso más pequeños en las computadoras notebook). En cada plato se deposita un óxido de metal delgado magnetizable. Después de su fabricación, no hay información de ninguna clase en el disco. Antes de poder utilizar el disco, cada plato debe recibir un formato de bajo nivel mediante software. El formato consiste en una serie de pistas concéntricas, cada una de las cuales contiene cierto número de sectores, con huecos cortos entre los sectores.El formato de un sector se muestra en la figura 5-25. 




La posición del sector 0 en cada pista está desfasada de la pista anterior cuando se aplica el formato de bajo nivel. Este desplazamiento, conocido como desajuste de cilindros, se realiza para mejorar el rendimiento. La idea es permitir que el disco lea varias pistas en una operación continua sin perder datos. La naturaleza de este problema se puede ver en la figura 5-19(a). Suponga que una petición necesita 18 sectores, empezando en el sector 0 de la pista más interna. Para leer los primeros 16 sectores se requiere una rotación de disco, pero se necesita una búsqueda para desplazarse una pista hacia fuera para llegar al sector 17. Para cuando la cabeza se ha desplazado una pista, el sector 0 ha girado más allá de la cabeza, de manera que se necesita una rotación para que vuelva a pasar por debajo de la cabeza. Este problema se elimina al desviar los sectores como se muestra en la figura 5-26. 





Hardware de Disco

Los discos son de varios tipos. Los más comunes son los discos magnéticos (discos duros y flexibles). Se caracterizan por el hecho de que las operaciones de lectura y escritura son igual de rápidas, lo que los hace ideales como memoria secundaria (como paginación o sistemas de archivos, por ejemplo). Algunas veces se utilizan arreglos de estos discos para ofrecer un almacenamiento altamente confiable. Para la distribución de programas, datos y películas, son también importantes varios tipos de discos ópticos (CD-ROMs, CD-grabable y DVD). En las siguientes secciones describiremos primero el hardware y luego el software para estos dispositivos.
Discos magnéticos
Los discos magnéticos se organizan en cilindros, cada uno de los cuales contiene tantas pistas como cabezas apiladas en forma vertical. Las pistas se dividen en sectores. El número de sectores alrededor de la circunferencia es por lo general de 8 a 32 en los discos flexibles, y hasta varios cientos en los discos duros. El número de cabezas varía entre 1 y 16. Los discos antiguos tienen pocos componentes electrónicos y sólo producen un flujo de bits serial simple. En estos discos el controlador realiza la mayor parte del trabajo. En otros discos, en especial los discos IDE (Electrónica de Unidad Integrada) y SATA (ATA Serial), la unidad de disco contiene un microcontrolador que realiza un trabajo considerable y permite al controlador real emitir un conjunto de comandos de nivel superior. A menudo el controlador coloca las pistas en caché, reasigna los bloques defectuosos y mucho más. Una característica de dispositivo que tiene implicaciones importantes para el software controlador del disco es la posibilidad de que un controlador realice búsquedas en dos o más unidades al mismo tiempo. Éstas se conocen como búsquedas traslapadas. Mientras el controlador y el software esperan a que se complete una búsqueda en una unidad, el controlador puede iniciar una búsqueda en otra unidad. Muchos controladores también pueden leer o escribir en una unidad mientras buscan en otra u otras unidades, pero un controlador de disco flexible no puede leer o escribir en dos unidades al mismo tiempo (para leer o escribir, el controlador tiene que desplazar bits en una escala de tiempo en microsegundos, por lo que una transferencia ocupa la mayor parte de su poder de cómputo). Esta situación es distinta para los discos duros con controladores integrados, y en un sistema con más de una de estas unidades de disco duro pueden operar de manera simultánea, al menos en cuanto a la transferencia de datos entre el disco y la memoria de búfer del controlador. Sin embargo, sólo es posible una transferencia entre el controlador y la memoria principal. La capacidad de realizar dos o más operaciones al mismo tiempo puede reducir el tiempo de acceso promedio de manera considerable. En la figura 5-18 se comparan los parámetros del medio de almacenamiento estándar para la IBM PC original con los parámetros de un disco fabricado 20 años después, para mostrar cuánto han cambiado los discos en 20 años. Es interesante observar que no todos los parámetros han mejorado tanto. El tiempo de búsqueda promedio es siete veces mejor de lo que era antes, la velocidad de transferencia es 1300 veces mejor, mientras que la capacidad aumentó por un factor de
50,000. Este patrón está relacionado con las mejoras relativamente graduales en las piezas móviles, y densidades de bits mucho mayores en las superficies de grabación.



E/S Mediante el Uso de DMA

El acceso directo a memoria (DMA, del inglés direct memory access) permite a cierto tipo de componentes de una computadora acceder a la memoria del sistema para leer o escribir independientemente de la unidad central de procesamiento (CPU) principal. Muchos sistemas hardware utilizan DMA, incluyendo controladores de unidades de disco, tarjetas gráficas y tarjetas de sonido. DMA es una característica esencial en todos los ordenadores modernos, ya que permite a dispositivos de diferentes velocidades comunicarse sin someter a la CPU a una carga masiva de interrupciones.
Una transferencia DMA consiste principalmente en copiar un bloque de memoria de un dispositivo a otro. En lugar de que la CPU inicie la transferencia, esta se lleva a cabo por el controlador DMA. Un ejemplo típico es mover un bloque de memoria desde una memoria externa a una interna más rápida. Tal operación no ocupa al procesador y, por ende, éste puede efectuar otras tareas. Las transferencias DMA son esenciales para aumentar el rendimiento de aplicaciones que requieran muchos recursos.
Cabe destacar que aunque no se necesite a la CPU para la transacción de datos, sí se necesita el bus del sistema (tanto bus de datos como bus de direcciones), por lo que existen diferentes estrategias para regular su uso, permitiendo así que no quede totalmente acaparado por el controlador DMA.

Drivers de Dispositivos

Al principio de este capítulo analizamos lo que hacen los drivers. Vimos que cada controlador tiene
ciertos registros de dispositivos que se utilizan para darle comandos o ciertos registros de dispositivos
que se utilizan para leer su estado, o ambos. El número de registros de dispositivos y la
naturaleza de los comandos varían radicalmente de un dispositivo a otro. Por ejemplo, un driver de
ratón tiene que aceptar información del ratón que le indica qué tanto se ha desplazado y cuáles botones
están oprimidos en un momento dado. Por el contrario, un driver de disco tal vez tenga que
saber todo acerca de los sectores, pistas, cilindros, cabezas, movimiento del brazo, los propulsores
del motor, los tiempos de asentamiento de las cabezas y todos los demás mecanismos para hacer
que el disco funcione en forma apropiada. Obviamente, estos drivers serán muy distintos.
Como consecuencia, cada dispositivo de E/S conectado a una computadora necesita cierto código
específico para controlarlo. Este código, conocido como driver, es escrito por el fabricante del
dispositivo y se incluye junto con el mismo. Como cada sistema operativo necesita sus propios drivers,
los fabricantes de dispositivos por lo común los proporcionan para varios sistemas operativos
populares.
Cada driver maneja un tipo de dispositivo o, a lo más, una clase de dispositivos estrechamente
relacionados. Por ejemplo, un driver de disco SCSI puede manejar por lo general varios discos
SCSI de distintos tamaños y velocidades, y tal vez un CD-ROM SCSI también. Por otro lado, un
ratón y una palanca de mandos son tan distintos que por lo general se requieren controladores diferentes.
Sin embargo, no hay una restricción técnica en cuanto a que un driver controle varios dispositivos
no relacionados. Simplemente no es una buena idea.
Para poder utilizar el hardware del dispositivo (es decir, los registros del controlador físico), el
driver por lo general tiene que formar parte del kernel del sistema operativo, cuando menos en las
arquitecturas actuales. En realidad es posible construir controladores que se ejecuten en el espacio
de usuario, con llamadas al sistema para leer y escribir en los registros del dispositivo. Este diseño
aísla al kernel de los controladores, y a un controlador de otro, eliminando una fuente importante
de fallas en el sistema: controladores con errores que interfieren con el kernel de una manera u otra.
Para construir sistemas altamente confiables, ésta es, en definitiva, la mejor manera de hacerlo. Un
ejemplo de un sistema donde los controladores de dispositivos se ejecutan como procesos de usuario
es MINIX 3. Sin embargo, como la mayoría de los demás sistemas operativos de escritorio esperan
que los controladores se ejecuten en el kernel, éste es el modelo que consideraremos aquí.
Como los diseñadores de cada sistema operativo saben qué piezas de código (drivers) escritas
por terceros se instalarán en él, necesita tener una arquitectura que permita dicha instalación. Esto
implica tener un modelo bien definido de lo que hace un driver y la forma en que interactúa con el
resto del sistema operativo. 

Manejador de Interrupciones

Aunque la E/S programada es útil algunas veces, para la mayor parte de las operaciones de E/S las
interrupciones son un hecho incómodo de la vida y no se pueden evitar. Deben ocultarse en la profundidad
de las entrañas del sistema operativo, de manera que éste sepa lo menos posible de ellas.
La mejor manera de ocultarlas es hacer que el controlador que inicia una operación de E/S se bloquee
hasta que se haya completado la E/S y ocurra la interrupción. El controlador se puede bloquear
a sí mismo realizando una llamada a down en un semáforo, una llamada a wait en una variable de
condición, una llamada a receive en un mensaje o algo similar, por ejemplo.
Cuando ocurre la interrupción, el procedimiento de interrupciones hace todo lo necesario para poder
manejarla. Después puede desbloquear el controlador que la inició. En algunos casos sólo completará
up en un semáforo. En otros casos realizará una llamada a signal en una variable de
condición en un monitor. En otros más enviará un mensaje al controlador bloqueado. En todos los
casos, el efecto neto de la interrupción será que un controlador que estaba bloqueado podrá ejecutarse
ahora. Este modelo funciona mejor si los controladores están estructurados como procesos del
kernel, con sus propios estados, pilas y contadores del programa.
Desde luego que en realidad esto no es tan simple. Procesar una interrupción no es cuestión de
sólo tomar la interrupción, llamar a up en algún semáforo y después ejecutar una instrucción IRET
para regresar de la interrupción al proceso anterior. Hay mucho más trabajo involucrado para el sistema
operativo. Ahora veremos un esquema de este trabajo como una serie de pasos que se deben
llevar a cabo en el software, una vez que se haya completado la interrupción de hardware.
A continuación tal vez no sean necesarios en una máquina específica, y tal vez se requieran otros
que no estén listados. Además, los pasos que se llevan a cabo pueden estar en distinto orden en algunas
máquinas.

1. Guardar los registros (incluyendo el PSW) que no han sido guardados por el hardware de
la interrupción.
2. Establecer un contexto para el procedimiento de servicio de interrupciones. Para ello tal
vez sea necesario establecer el TLB, la MMU y una tabla de páginas.
3. Establecer una pila para el procedimiento de servicio de interrupciones.
4. Reconocer el controlador de interrupciones. Si no hay un controlador de interrupciones
centralizado, rehabilitar las interrupciones.
5. Copiar los registros desde donde se guardaron (posiblemente en alguna pila) a la tabla de
procesos.
6. Ejecutar el procedimiento de servicio de interrupciones. Éste extraerá información de los
registros del controlador de dispositivos que provocó la interrupción.
7. Elegir cuál proceso ejecutar a continuación. Si la interrupción ha ocasionado que cierto
proceso de alta prioridad que estaba bloqueado cambie al estado listo, puede elegirse para
ejecutarlo en ese momento.
8. Establecer el contexto de la MMU para el proceso que se va a ejecutar a continuación.
También puede ser necesario establecer un TLB.
9. Cargar los registros del nuevo proceso, incluyendo su PSW.
10. Empezar a ejecutar el nuevo proceso.

E/S Controlada por Interrupciones

Ahora vamos a considerar el caso de imprimir en una impresora que no coloca los caracteres en un
búfer, sino que imprime cada uno a medida que va llegando. Si la impresora puede imprimir (por
ejemplo,) 100 caracteres/seg, cada carácter requiere 10 mseg para imprimirse. Esto significa que
después de escribir cada carácter en el registro de datos de la impresora, la CPU estará en un ciclo
de inactividad durante 10 mseg, esperando a que se le permita imprimir el siguiente carácter. Este
tiempo es más que suficiente para realizar un cambio de contexto y ejecutar algún otro proceso durante
los 10 mseg que, de otra manera, se desperdiciarían.
La forma de permitir que la CPU haga algo más mientras espera a que la impresora esté lista es
utilizar interrupciones. Cuando se realiza la llamada al sistema para imprimir la cadena, el búfer se
copia en espacio de kernel (como vimos antes) y el primer carácter se copia a la impresora, tan pronto
como esté dispuesta para aceptar un carácter. En ese momento, la CPU llama al planificador y se
ejecuta algún otro proceso. El proceso que pidió imprimir la cadena se bloquea hasta que se haya impreso
toda la cadena.




Software de Salida

Ahora consideremos el software de salida. Primero analizaremos una salida de ejemplo a una ventana de texto, que es lo que los programadores...