SISTEMA OPERATIVO MH 1.5 Carlos Villarrubia Luis Jim‚nez Eduardo Dom¡nguez Escuela Universit ria de Inform tica Universidad de Castilla - La Mancha Ciudad Real, Espa¤a 1.- Introducci¢n MH es un sistema operativo multihilo creado con fines docentes. Sus objetivos son ilustrar de la forma m s sencilla los conceptos te¢ricos comunes en los sistemas operativos modernos con un ejemplo real. Por comodidad de desarrollo, MH necesita del sistema operativo MS- DOS para su inicializaci¢n. Una vez inicializado MS-DOS no se utiliza nunca e inclusive MH inhibe cualquier llamada a MS-DOS por parte de los hilos del sistema. MH es un sistema monoproceso con capacidad para la gesti¢n de varios hilos de ejecuci¢n de ese proceso. Las llamadas al sistema est n divididas en dos grupos: Un grupo para la creaci¢n, destrucci¢n y gesti¢n de hilos y otro grupo para la entrada/salida a trav‚s de la consola del sistema. El £nico proceso del sistema corresponde a la imagen del programa init. A partir de este proceso se pueden crear hilos de ejecuci¢n adicionales de este proceso. De forma adicional se tienen tres funciones para realizar entrada y salida por la consola del sistema. 2.- Arquitectura del sistema El dise¤o de MH corresponde a un sistema operativo monol¡tico con una divisi¢n en m¢dulos seg£n el objetivo de las variables y funciones del sistema. El sistema esta dividido en los siguientes m¢dulos : consola : Incluye todo el tratamiento y funciones para la lectura y escritura de caracteres por la consola del sistema. hilos : Constituye la implementaci¢n de la gesti¢n de los hilos del sistema. lista : Tiene funciones para la manipulaci¢n de las diferentes listas de hilos. memoria : Contiene el c¢digo para la inicializaci¢n y asignaci¢n de memoria del sistema. varios : Funciones de utilidad no especificas de MH. Adicionalmente se tiene un m¢dulo (mh.c) con la inicializaci¢n del sistema. 3.- Gesti¢n de procesos MH s¢lo tiene un proceso (proceso init) con un hilo de ejecuci¢n asociado y un espacio de memoria. Este proceso puede de forma din mica crear y destruir hilos de ejecuci¢n adicionales. Un hilo de ejecuci¢n tiene pocos recursos asociados de forma individual. Exactamente un hilo de ejecuci¢n s¢lo posee de forma individual una entrada en la tabla de hilos y una pila de tama¤o predeterminado. La estructura de datos m s importante para la gesti¢n de hilos es la tabla de hilos (extern THilo thTablaHilos[NUMMAXHILOS])cuyos elementos son del tipo THilo. La implementaci¢n de la tabla de hilos se realiza con un array con un tama¤o m ximo. La identificaci¢n de cada hilo es a trav‚s del indice del hilo en el array. typedef struct { int estado; /* Estado del hilo */ int tipo; /* Tipo de hilo: nucleo o usuario */ int modo; /* Modo de ejecucion: nucleo o usuario */ unsigned ss; /* Segmento de pila */ unsigned isp; /* Puntero inicial de pila */ unsigned sp; /* Puntero de pila en modo */ int far *pmarca; /* Puntero a la marca del segmento de pila */ TRegsPila far *pregs; /* Puntero a la estructura inicial de registros */ THiloFunc pfunc; /* Puntero a la funcion de un hilo del nucleo */ int arg; /* Argumento de la funcion de un hilo del nucleo */ unsigned long diftick; /* Diferencia de ticks para hilo dormido */ int antid; /* Indice del anterior hilo en la lista */ int sigid; /* Indice al hilo posterior en la lista */ } THilo; A su vez tenemos otras variables £tiles para la gesti¢n de hilos como int nHiloActual que indica el hilo actual en ejecuci¢n e int hHiloAnterior que indica el anterior hilo en ejecuci¢n. MH utiliza dos clases de hilos de ejecuci¢n : hilos del n£cleo e hilos de usuario. La £nica diferencia entre ellos es que un hilo del n£cleo utiliza el espacio de direcciones de memoria del n£cleo y un hilo de usuario el espacio de direcciones de memoria del proceso del usuario (init). Los estados de los hilos son : EJECUCION, LISTO, DORMIDO y BLOQUEADO. Un hilo s¢lo puede estar en una lista, por lo tanto, los campos antid y sigid indica el antecesor y sucesor. Todo el estado de un hilo no se guarda en su estructura de la tabla de hilos. Cuando un hilo esta en un estado diferente a ejecuci¢n el contenido de los registros del procesador excepto SS y SP se guarda en la cima de la pila. A tal efecto se utiliza la definici¢n de funciones de manejo de interrupciones del compilador. De tal forma que una declaraci¢n del tipo : void interrupt manejador(void)tiene un inicio y fin con la siguiente secuencia de instrucciones m quina. push ax push bx push cx push dx push es push ds push si push di push bp teniendo en cuenta que cuando se produce una interrupci¢n el procesador introduce el contenido de los registro de flags, CS e IP en la pila antes del inicio del manejador de interrupci¢n tenemos la siguiente situaci¢n de la pila antes de la ejecuci¢n del manejador. Cima de la pila Š BP DI SI DS ES DX CX BX AX IP CS FLAGS cuando acaba la funci¢n de manejo de la interrupci¢n el compilador introduce las siguientes instrucciones m quina para recuperar el contexto de ejecuci¢n del hilo. pop bp pop di pop si pop ds pop es pop dx pop cx pop bx pop ax iret donde iret recupera los valores de los registro IP, CS y FLAGS de la pila. Con este esquema la conmutaci¢n de contexto entre dos hilos se realiza de forma autom tica por las funcines definidas como manejadores de interrupciones donde en su cuerpo se cambia los valores de control de la pila del procesador (SS y SP). void interrupt ConmutarContexto(void) { thTablaHilos[nHiloAnterior].sp = _SP; _SS = thTablaHilos[nHiloActual].ss; _SP = thTablaHilos[nHiloActual].sp; } La manipulaci¢n del contexto de ejecuci¢n de un proceso se realiza a trav‚s del campo estructura pregs de su entrada a la tabla de hilos que apunta a la cima de la pila que tiene tantos campos como registros el procesador excepto SS y SP. Estos campos son particularmente importantes en la inicializaci¢n de los hilos. La planificaci¢n de los hilos se realiza en la funci¢n void Planificador(void)donde se manipula la lista de hilos en estado de preparados para ejecuci¢n Tlista tlHilosPreparados y se actualiza el valor de las variables nHiloActual y nHiloAnterior. La planificaci¢n puede ser preventiva o no en funci¢n de la variable bPreventivo. Si esta variable tiene un valor distinto de 0 el manejador del reloj expulsara del procesador a este hilo cuando haya consumido TICKSQUANTUM ticks consecutivos de reloj. El n£mero de ticks de reloj por segundo se define en la inicializaci¢n con la variable lHZReloj que puede tener un valor m¡nimo de 19 ticks/segundo y un valor m ximo de 1.193.180 ticks/segundo aunque el l¡mite real estar  en funci¢n de la velocidad del procesador del sistema (t¡picamente a un m ximo de 100.000 ticks/segundo en un sistema Pentium 133). 4.- Gesti¢n de memoria La gesti¢n de memoria es muy simple. En la inicializaci¢n calcula la memoria total del sistema y va asignando memoria para las pilas de todos los hilos posibles del sistema. No existe posibilidad en la versi¢n actual de tener un manejo de memoria din mico. Cada hilo tiene una pila con un tama¤o predeterminado que se asigna en la inicializaci¢n. Al fin de cada pila existe una marca con un valor predefinido que se compara peri¢dicamente para detectar si ese hilo ha utilizado m s zona de pila. 5.- Gesti¢n de entrada/salida Los £nicos dispositivos perif‚ricos utilizados por MH corresponde al teclado y la pantalla asociado al computador. A este par de dispositivos se les menciona como consola del sistema. La utilizaci¢n de la pantalla se simple pues es un dispositivo direccionable por posiciones de memoria. En la inicializaci¢n el sistema reconoce el modo gr fico de la tarjeta (monocrom tico o color) que es utilizado posteriormente. El teclado de la consola es el £nico dispositivo que puede dejar a un hilo en estado de BLOQUEADO. Esto es debido a que es posible no tener ning£n car cter en el buffer de lectura del teclado. 6.- Creaci¢n de programas para MH El compilador utilizado para el sistema operativo MH y sus programas de aplicaci¢n ha sido Borland C++ 3.1. Este entorno de programaci¢n por defecto genera aplicaciones para el sistema operativo MS-DOS. En el caso del desarrollo del programa init se tienen que utilizar los pr¢logos y ep¡logos de MH y no de MS-DOS. Este se realiza enlazando el modulo ccom.obj a los archivos objetos del programa init. Adicionalmente se necesita el archivo objeto sistema.o y el archivo de cabecera sistema.h para realizar las llamadas al sistema operativo MH. A tal efecto se utiliza un archivo por lotes ccom.bat en el directorio init que automatiza estas tareas incluyendo la inclusi¢n de la cabecera con los tama¤os de las distintas regiones del programa (c¢digo, datos y pila). A tal efecto se debe consultar el archivo init.map para saber el tama¤o de estas  reas. Todas las llamadas al sistema se realizan a trav‚s de la interrupci¢n 134 y los argumentos de las llamadas se pasan a trav‚s de los registros del procesador. Para facilitar la programaci¢n con MH se tiene un archivo sistema.c que realiza estas operaciones y ofrece un entorno de programaci¢n en base a funciones C. Las diferentes llamadas al sistema con su interfaz en C son : Gesti¢n de hilos : int MHNuevoHilo(pHiloFunc pHFuncion, int nArg, unsigned uiPrologo); int MHTerminarHilo(int IdHilo); int MHFinHilo(void); int MHDormirHilo(unsigned long NumTicks); Gesti¢n de la consola : char MHLeerCaracter(void); int MHEscribirCadena(const char far *c); int MHEscribirCaracter(char c); Un ejemplo de proceso init con la creaci¢n de tres hilos de ejecuci¢n que imprimen concurrentemente tres caracteres diferentes ser¡a : #include "sistema.h" void Hilo(int narg) { int i; for (i=0 ; i<300; i++) MHEscribirCaracter('A' + narg); } void main(void) { MHNuevoHilo(Hilo, 0); MHNuevoHilo(Hilo, 1); MHNuevoHilo(Hilo, 2); } Como se puede observar, se utiliza la funci¢n de creaci¢n de hilos MHNuevoHilo con un argumento menos que en la llamada al sistema an loga. Esto es debida a que el entorno de desarrollo para MH proporciona un prologo com£n para todos los hijos de ejecuci¢n y no es necesario especificarlo.