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.
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. [Tan92].
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 14Tabla 6.1 Direcciones de Mapeo de Entrada - Salida
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.
6.4.1 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.
6.4.2 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.
6.4.3 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.
6.4.4 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.
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).