Últimos Cambios |
||
Blog personal: El hilo del laberinto |
Última Actualización:
28 de Junio de 1.996 - Viernes
Este Proyecto Fin de Carrera, por sus propias características, se presta muy bien a una implementación multitarea. Por una parte existen multitud de procesos autocontenidos que intercambian información entre sí mediante el uso de una interfaz bien definida y coherente. Por otra parte la ejecución de estos mismos procesos es, en la mayor parte de los casos, asíncrona. Es decir, la ejecución está dirigida por eventos externos tales como el vencimiento de temporizaciones, la recepción de datagramas a través de alguno de los puertos hardware, la llegada de mensajes provenientes de otros procesos, etc.
Lo ideal, pues, hubiese sido implementar todo el Proyecto en un entorno con capacidad multiproceso. No obstante el objetivo era desarrollar las rutinas de la forma más portable posible y la multitarea no es una funcionalidad disponible en la mayoría de los sistemas personales. Fue por ello por lo que, contando con la experiencia del autor en el proyecto POWERNET [POWERNET] y su continuación en Laboratorio de Comunicación de Datos [LABCD][LABCD-MSG] del 5° Curso en la E.T.S.I.T. de Vigo, se decidió desarrollar un minisistema operativo multitarea, portable, eficiente y fácil de utilizar, sobre el cual implantar este Proyecto en sí.
El resultado de este esfuerzo es el módulo de gestión de mensajes. Gracias a él es posible definir procesos independientes y vías de comunicación entre los mismos. Sus características principales son:
Este módulo proporciona dos funcionalidades básicas: gestión de procesos e intercambio de mensajes entre los mismos. Para ello se han definido varias estructuras de datos.
proc_id
Este tipo define el identificador de proceso. Cada proceso declarado posee un identificador único a través del cual se puede comunicar con el resto de los procesos del sistema.
msg_id
Este tipo define el identificador de mensaje. Todos los mensajes intercambiados entre los diferentes procesos deben cumplir una serie de normas estrictas que aseguren que el proceso receptor sea capaz de reconocer y procesar adecuadamente los mensajes que recibe.
Los mensajes en sí consisten en estructuras como la siguiente:
typedef struct { proc_id destino; proc_id remitente; msg_id mensaje; campo campo1; campo campo2; campo campo3; } msg; typedef union { pmbuf puntero; uint32 valor; } campo;
Un proceso que desee enviar un mensaje debe indicar en el campo "destino" el identificador del proceso destinatario. El campo "remitente" es inicializado de forma automática por el módulo de gestión de mensajes y permite implementar un esquema de control de acceso. A "mensaje" se le asigna el identificador o tipo de mensaje que se desea enviar, y los campos de datos constituyen los parámetros del mensaje en sí. Como puede observarse, cada campo puede estar constituido por un valor de 32 bits o por un puntero a una estructura MBUF. El significado y contenido de cada parámetro es específico de cada tipo de mensaje en particular.
Los mensajes definidos en este módulo y, por tanto, globales al sistema, son:
MSG_INIT
El objetivo de este mensaje es ser utilizado en un Broadcast para inicializar todos los procesos.
Los valores de los parámetros no tienen ninguna finalidad específica.
MSG_QUIT
El objetivo de este mensaje es ser utilizado en un Broadcast para finalizar todos los procesos. Cuando todos los procesos han terminado de gestionar este mensaje, el dispatcher da por finalizada su ejecución y el programa termina.
Los valores de los parámetros no tienen ninguna finalidad específica.
MSG_IDLE
Este mensaje es recibido por los procesos IDLE cuando no existe ninguna otra tarea útil.
Los valores de los parámetros son indefinidos.
MSG_MBUF
Este es el mensaje empleado cuando un proceso desea transferir a otro la propiedad de un bloque de memoria. El campo1 contiene el puntero a la estructura MBUF que especifica el bloque en cuestión. El resto de los campos no tienen una finalidad específica y pueden ser utilizados para transferir información adicional.
MSG_TIMEOUT
Este mensaje se ha definido con el fin de indicar el término de una temporización. No obstante no hay ninguna obligación de utilizar este mensaje en concreto; el sistema permite retener cualquier mensaje por un tiempo dado antes de su entrega a su destinatario final.
Las rutinas del sistema de gestión de mensajes accesibles desde el exterior son:
void msg_init(void);
Esta rutina es la primera, de este módulo, que debe llamarse. Es la responsable de inicializar la tabla de procesos y las colas de mensajes.
proc_id msg_proc_alta(puint8 id,void (FPOINTER proc)(pmsg), uint8 tipo,uint8 prio);
Esta rutina da de alta un proceso y devuelve su identificador. El parámetro "id" proporciona un nombre explicativo asociado al proceso, utilizado para el profiling y la gestión de errores fatales. "proc" es un puntero a una función con el siguiente prototipo:
void proc(pmsg mensaje);
Dicha rutina constituye el punto de entrada al proceso. Es la que recibe los mensajes del dispatcher y la que éste invoca cuando la ejecución corresponde a ese proceso. "tipo" especifica el tipo de proceso que estamos creando:
Los tres últimos tipos pueden combinarse para crear procesos especiales. "TIPO_INIT" especifica que el proceso en cuestión debe recibir todos los mensajes Broadcast SALVO los "MSG_QUIT". Estos serán recibidos sólo si el proceso es "TIPO_QUIT". En cuanto a "TIPO_IDLE", un proceso con esta característica se ejecutará también cuando no exista ningún otro no "IDLE" compitiendo por la CPU. Ello resulta muy útil, por ejemplo, para la gestión de los puertos de comunicaciones o para implementar esquemas de "recogida de basuras" (Garbage Collection) en background.
Por último el parámetro "prio" fija la prioridad del proceso. Esta prioridad puede variarse de forma dinámica, y su valor puede ser:
Un proceso de alta prioridad se ejecutará siempre que haya un mensaje pendiente para él. Un proceso de baja prioridad, en cambio, sólo se ejecuta cuando haya un mensaje pendiente para él y no haya ningún proceso de alta prioridad esperando.
A los procesos "TIPO_INIT" o "TIPO_QUIT" se les asigna una prioridad alta independientemente de lo que se indique en el campo correspondiente.
Todos los procesos "TIPO_IDLE", cuando la CPU no tiene otras tareas que realizar, se ejecutan según una disciplina Round Robin, independientemente de su prioridad.
void msg_proc_baja(proc_id id);
Este procedimiento permite dar de baja un proceso especificado por su identificador.
void msg_send(pmsg mensaje);
Esta rutina es ejecutada en el contexto del proceso llamante, aunque también puede ser invocada antes de lanzar el dispatcher para, por ejemplo, enviar los mensajes Broadcast iniciales necesarios para arrancar correctamente todos los módulos. Sirve para enviar un mensaje a un proceso. Todos los campos, salvo el del remitente, deben ser inicializados por el proceso transmisor.
void msg_diferido(pmsg mensaje,uint32 milisec);
Esta rutina es la encargada, entre otras cosas, de realizar temporizaciones. Su funcionamiento real consiste en almacenar un mensaje durante un tiempo determinado antes de entregarlo. Cada proceso receptor sólo puede tener pendiente un mensaje diferido. Si se intentan enviar otros sólo se conserva el último. El tiempo se especifica en milisegundos. Un tiempo de CERO milisegundos borra cualquier mensaje que estuviese pendiente de entrega.
void msg_dispatcher(void);
Esta rutina constituye el núcleo del sistema de gestión de mensajes. Debe ser lanzada tras "msg_init" y tras declarar algunos procesos y los mensajes iniciales. Se encarga de enrutar los mensajes y ejecutar los procesos invocados. Comprueba, así mismo, el vencimiento de las temporizaciones en curso y realiza una planificación de CPU en función de los mensajes pendientes de entrega y las prioridades de los procesos. Durante los tiempos muertos se lanzan procesos "IDLE".
Esta rutina se ejecuta hasta que alguna entidad envía un "MSG_QUIT" Broadcast y todos los procesos "TIPO_QUIT" terminan su gestión.
Una vez que finaliza imprime una serie de datos de profiling muy pormenorizados: Porcentaje de tiempo asignado a cada proceso, tiempo medio de espera en cola para los mensajes de cada prioridad, tiempo total invertido en procesos "IDLE" y tiempo total desperdiciado debido a la propia sobrecarga que supone la ejecución del dispatcher.
Adicionalmente se han definido algunos tipos de datos, con fines de apoyo:
PROC_BROADCAST
Cuando un proceso desea enviar un mensaje Broadcast debe especificar este valor en el campo "destino". No se permite enviar mensajes Broadcast diferidos. Los mensajes Broadcast son recibidos por los procesos "TIPO_INIT", salvo si se trata de un mensaje "MSG_QUIT". En dicho caso los receptores serán los procesos "TIPO_QUIT".
PROC_INDEF
Este identificador indica, en el campo "remitente", que no se sabe qué proceso ha enviado el mensaje. Es el caso, por ejemplo, de los mensajes iniciales enviados antes de invocar a la rutina "msg_dispatcher" y antes, por tanto, de que existan procesos propiamente dichos.
proc_id PROC_SELF;
Esta variable contiene el identificador del proceso actualmente en ejecución. Permite que una rutina conozca qué proceso la está ejecutando o bien que un proceso pueda enviarse mensajes a sí mismo o indicar su identidad a otro de forma simple.
puint8 PROC_ID_SELF;
Esta variable es un puntero a la cadena indicada cuando se
declaró el proceso actualmente en ejecución y que, se supone,
proporciona información útil sobre el mismo (habitualmente el
nombre). Este dato es utilizado, por ejemplo, para la impresión
de los mensajes de diagnóstico ante errores fatales.
Implementación
Para cada proceso declarado se asigna una estructura interna que contiene toda la información relevante referente a dicho proceso. Esta información incluye tanto los datos especificados al darlo de alta como campos destinados a la gestión del profiling o la entrega de mensajes diferidos. Asimismo se mantienen dos colas de mensajes (una por cada prioridad) que contienen los mensajes en espera de ser entregados e información sobre su instante de llegada al sistema.
Los pasos que realiza el dispatcher son, aproximadamente, los siguientes:
Los mensajes de cada cola son entregados estrictamente en el orden de llegada al sistema.
Dado que se ha implementado un esquema de multitarea cooperativa no puede garantizarse que los mensajes diferidos sean entregados exactamente en el momento especificado. El instante de entrega viene determinado por la carga del sistema, la precisión del reloj y la prioridad del proceso.
En cualquier caso lo que sí se garantiza es que la entrega nunca se realizará antes del tiempo demandado. Esto resulta muy útil para implementar los TimeOut's.
Por último, se considera la prioridad de los procesos sólo cuando
se les envían los mensajes. Un cambio en la prioridad no les
afecta una vez que estos han sido almacenados en una cola.
Bibliografía
[LABCD] Laboratorio de Comunicación de Datos Curso 94-95. Vigo, 16 de Marzo de 1.995 Alvaro Manuel Gómez Vieites Carlos Gabieiro Martínez Jose Cadavid Jáuregui Jesús Cea Avión [LABCD-MSG] Laboratorio de Comunicación de Datos Curso 94-95. Vigo, 16 de Marzo de 1.995 Módulo de Mensajes Alvaro Manuel Gómez Vieites Carlos Gabieiro Martínez Jose Cadavid Jáuregui Jesús Cea Avión [POWERNET] Proyecto PowerNet Jesús Cea Avión Implementación de multitarea cooperativa en lenguaje C 11 de Abril de 1.990
Más información sobre los OpenBadges
Donación BitCoin: 19niBN42ac2pqDQFx6GJZxry2JQSFvwAfS