Wind River – Vxworks QNX Sistemas Operativos En Tiempo Real Elección de un sistema operativo Requerimientos de una aplicación en tiempo real : • • • • • Acceder a dispositivos de E/S a bajo nivel Trabajar con interrupciones Medir y manipular el tiempo Organitzar la aplicación en tareas de ejecución concurrente. Permitir elegir y parametrizar el algoritmo de planificación, preferentemente basado en prioridades. • Disponer con eficientes mecanismos de comunicación y sincronismo. • Los parámetros temporales del entorno de ejecución son pequeños y conocidos ( cambios de contexto e interrupciones).. – Ejemplos de SOTR pur0s: QNX, VxWorks, LynxOS, RTEMS – Exemplos de extensiones de tiempo real:: Windows CE, RTX-Win NT, RTLinux Programación de periféricos • Periférico – Disposito que permite a un sistema basado en microprocessador realitzar una serie de acciones no consideradas de cálculo o de transferencia con memoria. – Ejemplos: • • • • • Interfícies digitales de entrada y salida Interfícies analógicas de entrada y salida Interfícies de comunicaciones Computadores/temporitzadores Gestores de interrupciones Organización interna: registros de programación Accions básicas con periféricos • Configuración : Ejemplos: velocitat de transmissió (RS-232), número de canal (CAD), iniciar conversió (CAD) • Estado Ejemplos: fin de conversión (CAD), error en la recepción(RS-232) • Transferencia de datos Ejemplos: datos adquiridos (CAD), mensaje recepción (RS-232) Interrupciones en QNX: ejemplo PCL PCL-812PG Lectura AD por interrupción: Variables compartidas: volatile unsigned char lowByte, highByte; unsigned int adrBase = 0x220; Rutina de servicio a la interrupción: const struct sigevent * isrPCL(void * area, int id) { highByte = in8(adrBase+5); lowByte = in8(adrBase+4); out8(adrBase+8,0); return NULL; } int main(void) { int id; unsigned char canal = 0, mode = 0x06; unsigned int dadaBin = 0; ThreadCtl(_NTO_TCTL_IO, 0); id = InterruptAttach(IRQ5,isrPCL,NULL,0,0); /* ...manca configurar el temporitzador... */ out8(adrBase+10,canal); out8(adrBase+11,mode); /* ...un cop rebuda la interrupció... */ InterruptLock(); dadaBin = highByte; dadaBin = ((dadaBin << 8) & 0x0F00) + lowByte; InterruptUnlock(); /* ...per finalitzar... */ out8(adrBase+11,0); InterruptDetach(id); } Tiempo La aplicación de control necesita coordinar la ejecución en el tiempo Necesidades: • Acceso a un reloj. • Medir el tiempo en intervalos. • Expresar retardos. • Programar accions periòdiques o aperiódicas. • Exemple amb retard relatiu en QNX: do #include <time.h> #include "sitr.h" #define PERIODE 0.01 #define DURADA 4.0 { codigo periodico /************************************* CODI int main(void) { struct timespec t0, t1, retardRelatiu; double tf; clock_gettime(CLOCK_REALTIME, &t0); tf = timespec2double(t0)+DURADA; retardRelatiu = double2timespec(PERIODE); ************************************** / clock_gettime(CLOCK_REALTIME, &t1); if (tf < timespec2double(t1)) break; conversión de timespec a double conversión de double a timespec clock_nanosleep(CLOCK_REALTIME, 0, &retardRelatiu, NULL); retardo } while (1); } Sistemas multitarea • Una tarea es un conjunto de instrucciones que se ejecutan secuencialmente. Normalmente son bucles infinitos • Las tareas son concurrentes entre ellas • En sistemes monoprocesador, el gestor se encarrega de la ejecución virtualmente paralela de las tareas. A nivel de instrucción máquina la ejecución es secuencial (monoprocesador) • En sistemas multiprocesadores o distribuidos la ejecución es realmente en paralelo, a nivel instrucción máquina. (comunicación, sincronización, protección de recursos mediante acceso exclusivo) •El SOTR proporciona el código que ejecutará concurrentemente estas tareas y las tareas del sistema. (gestor) – El gestor decide qué tarea se llevará a cabo. – El gestor va alternando en el CPU la ejecución de tareas. ( cambio de contexto). – Contexto: registros internos CPU + programa + información relevante a la tarea. asignación de CPU tarea A tarea B Cronograma tempo cambio de contexto: • guardar registros antes de desalojar la tarea B • restaurar registros cuando la tarea A va ser desalojada Planificación de tareas • Los algoritmos de planificación determinan la forma de repartir a lo largo del tiempo los CPU’s entre todas las tareas que forman una aplicación. Programación de una aplicación de tempo real • Acceder a dispositivos de E/S a bajo nivel • Trabajar con interrupciones • Medir y manipular el tiempo • Organizar la aplicación en tareas de ejecución concurrente. • Permitir elegir y parematrizar el algoritmo de planificación, preferentemente basado en prioridades. • Disponer de mecanismos de sincronización y comunicación. • Garantizar que los parámetros temporales del entorno de ejecución (e.j. cambios de contexto y servicio de interrupciones) son pequeños y conocidos. Requerimientos temporales • Los requerimientos temporales de STR se especifican normalmente en base a los seguientes parámetros: • el período, T • el termino, D • el tiempo de cómputo, C • el tiempo de respuesta, R tarea R C D A T tiempo (x 5 ms) • Tareas esporádicas (aperiódicas), T representa el tiempo mínimo entre dos activacions consecutivas • Se debe conseguir que todas las tareas se ejecuten dentro del término previsto (R ≤ D per todas las tareas) • Mecanismo de invocación: – Cireterio asignación CPU – Gestor cooperativo (cooperative scheduling): mientras la CPU está en posesión de una tarea, el gestor no puede intervenir hasta que la tareaa invoque explícitamente a través de la API (e.j., retardo), o bien acabe su ejecución. – Gestor apropiativo (preemptive scheduling): como el gestor cooperativo, frente a un acontecimiento, el gestor toma el control de la CPU, interrumpiendo la tarea en curso. g. aprop. g. coop. tasca A tasca A tasca B tasca B temps temps • • • El gestor apropiativo es más adecuado que el cooperativo per a un sistema de tempo real (STR) ya que el gestor puede controlar de manera centralitzada la respuesta las tareas. El gestor apropiativo round-robin es adecuado para sistemas multiusuario, se debe repartir de forma equitativa el CPU. El gestor apropiativo basado en prioridades es adecuado para un STR ya que es posible garantizar de forma determinista si los términos de respuesta van a cumplirse o no. Tareas en QNX. Ejemplo de multitarea • QNX permite organizar las tareas de forma concurrente. El gestor está implementado de forma transparente al usuario. La gestión es apropiativa basada en prioridades. • Toda aplicación (proceso) tiene como mínimo una tarea, que corresponde con el código de la función main(). • Una tasca puede crear otras tareas mediante la función pthread_create(). Por defecto, las nuevas tareas son creades con la misma prioridad. •Las tareas de igual prioridad se pueden gestionar de varias maneras, (por defecto round-robin). • Si una tarea crea otras tareas, antes de acabar, deberá esperar la finalitzación de las tareas creadas, mediante la función pthread_join(). Estados de las tareas en QNX – 19 estados de suspensión o bloqueo pthread_join() MsgSend() preparada execució clock_nanosleep() • Ejemplo: #include <pthread.h> void * controlador(void * c) { … clock_nanosleep(CLOCK_REALTIME, 0, &retardRelatiu, NULL); … } void * supervisor(void * s) {…} int main() { pthread_t tidS, tidC; … pthread_create(&tidS, NULL, supervisor, NULL); pthread_create(&tidC, NULL, controlador, NULL); … pthread_join(tidS, NULL); pthread_join(tidC, NULL); } g. rr--r nanoslee p controlador supervisor TS main join join temps Comunicación: mensajes • Introducció: – La interacción se realitza mediante las instrucciones envío (send()) y la recepción (receive()) – En sistemas con memoria compartida es un mecanismo alternativo de interacción, mientras que en sistemas distribuidos el paso de mensajes es la única alternativa. – Hay sincronización y en el proceso de comunicación hay como mínimo un emisor y un receptor. Ejemplo de envío de mensajes mensajes:: #include <stdio.h> typedef struct { double dada; } misDades; typedef struct { enum {OK, NOK} confirm; } misACK; int main(void) { int coid; int nd, pid, chid; FILE * fp; misDades mdad; misACK mack; fp=fopen("/net/odysseus/home/alumne/dat.cfg",“r"); fscanf(fp,"%d %d",&pid,&chid); fclose(fp); nd=netmgr_strtond("/net/odysseus",NULL); coid=ConnectAttach(nd,pid,chid,0,0); mdad.dada=123.456; MsgSend(coid,&mdad,sizeof(misDades),&mack,sizeof(mis ACK)); if (mack.confirm==NOK) printf("C: error de recepcio!!\n"); } Ejemplo de recepción de mensakes #include <stdio.h> typedef struct { double dada; } misDades; typedef struct { enum {OK, NOK} confirm; } misACK; int main(void) { int chidS; int rcvid; FILE * fp; misDades mdad; misACK mack; chidS=ChannelCreate(0); fp=fopen("/net/odysseus/home/alumne/dat.cfg",“w" ); fprintf(fp,"%d %d",getpid(),chidS); fclose(fp); rcvid=MsgReceive(chidS,&mdad,sizeof(misDades),NU LL); printf("S: %lf\n",mdad.dada); mack.confirm=NOK; MsgReply(rcvid,0,&mack,sizeof(misACK)); } Colas: • La comunicación és del tipus envío asíncrono. • Se debe activar el proceso de gestión de dolas. (mqueue) • La cola se crea con la funció mq_open(), y sus características. mq_send() mq_receive() Ejemplo de utilización de colas colas::envío #include <stdio.h> #include <mqueue.h> #define CAPACITAT_CUA 5 typedef struct { double dada; } misDades; int main(void) { mqd_t bustia; struct mq_attr qattr; misDades mdad; qattr.mq_maxmsg=CAPACITAT_CUA; qattr.mq_msgsize=sizeof(misDades); bustia=mq_open("/bustia", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR, &qattr); mdad.dada=123.456; mq_send( bustia, (char *)&mdad, sizeof(misDades), 0); printf(“Ll: %lf\n",mdad.dada); mq_close(bustia); } Ejemplo de colas colas:: Recepción #include <stdio.h> #include <mqueue.h> #define CAPACITAT_CUA 5 typedef struct { double dada; } misDades; int main(void) { mqd_t bustia; struct mq_attr qattr; misDades mdad; qattr.mq_maxmsg=CAPACITAT_CUA; qattr.mq_msgsize=sizeof(misDades); bustia=mq_open("/bustia", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR, &qattr); mq_receive( bustia, (char *)&mdad, sizeof(misDades), NULL); printf("R: %lf\n",mdad.dada); mq_close(bustia); mq_unlink("/bustia"); } Ejemplo, el seguente codigo crea una tarea controlador con prioridad 15 y mecanismo de planificación FIFO, cambia a round robin y prioridad 21: pthread_t tid; pthread_attr_t attr; struct sched_param newparam; pthread_attr_init(&attr); pthread_attr_setinheritsched(&attr,PTHREAD_EXPLICIT_SCHED); pthread_attr_setschedpolicy(&attr,SCHED_FIFO); attr.param.sched_priority = 15; pthread_create(&tid, &attr, controlador, NULL); ... newparam.sched_priority = 21; pthread_setschedparam(tid,SCHED_RR,&newparam); •Mecanismo Mutex: pthread_mutex_t mutex; pthread_mutexattr_t mutexattr; pthread_mutexattr_init(&mutexattr); pthread_mutexattr_setprotocol(&mutexattr,PTHREAD_PRIO_INHERI T); pthread_mutex_init(&mutex, &mutexattr); ...