C4 Semaphore TRL Yann DOUZE Polytech Paris UPMC - Section E2i3 Source : livre µc/osii de Jean-Jacques Labrosse et les cours de l école polytechnique de Montréal. Ressources Une ressource est une entité utilisée par une tâche : I/O devices: printer, keyboard, display memory: variable, structure, array Une ressource partagée est une entité utilisée par plusieurs tâches. Certaines ressources peuvent être corrompues si consultées par plusieurs tâches simultanément : Il faut obtenir l'accès exclusif à une telle ressource avant de l'utiliser. (mutual exclusion : mutex)
Mécanismes d exclusion mutuelle (1) L exclusion mutuelle est un mécanisme d accès exclusif à une ressource partagée ou à une section de code critique. Méthodes pour assurer l exclusion mutuelle: désactiver/activer les interruptions désactiver/activer l ordonnanceur Utiliser des sémaphores. Mécanismes d exclusion mutuelle (2) Désactivation des interruptions : void Function (void) OS_ENTER_CRITICAL(); // désactive les IRQ Accès aux ressources partagés OS_EXIT_CRITICAL(); // réactive les IRQ Affecte le temps de réponse du système. Approprié pour protection lors de variables partagées avec une ISR.
Mécanismes d exclusion mutuelle (3) Désactivation de l ordonnanceur : void Function (void) OSSchedLock();//désactive l ordonnanceur Accès aux ressources partagés OSSchedUnlock();//réactive l ordonnanceur Inefficace lors de variables partagées avec une ISR Le système a alors un comportement non préemptif. Mécanismes d exclusion mutuelle (4) opération de TestAndSet (Sans RTOS) Désactiver les interruptions Si ( Variable d accés = 0) Mettre la variable à 1; Réactiver les interruptions; Accéder à la resource; Remettre la variable à 0; Sinon // Vous n avez pas accès à la ressource. Réactiver les interruptions;
Semaphore Pour quoi faire? Contrôler l accès à une ressource partagé (exclusion mutuelle) Signalez l occurrence d un événements. Synchronisez deux taches. 2 types de sémaphore : Les sémaphores binaires (0 ou 1) Les sémaphores compteurs : 0 à max (255, 65535, ) Utilisé un semaphore pour accéder à une ressource partagé (1) Contrôle l accès à une ressource partagé : imprimante
Utilisé un semaphore pour accéder à une ressource partagé (2) OS_EVENT *ShareDataSem; void function (void) int err; OSSemPend(ShareDataSem, 0, &err); /*accès à la ressource partagé (les interruptions sont reconnus */ OSSemPost(ShareDataSem); Semaphore Caché
Semaphore Caché int ComSendCmd(char *cmd,char *response,int timeout) Acquérir le sémaphore; Envoyer la commande au device; Attente de réponse (avec timeout) Si (temps terminé) rendre le semaphore; return(error code); Sinon rendre le semaphore; return(no error) Le blocage mutuel: Deadlock Le blocage mutuel (deadlock) est la situation où deux tâches se bloquent mutuellement, chacune d entre elles attendant l accès à une ressource possédée par l autre.
Les mécanismes de communication entre taches (1) Les sémaphores utilisées pour la synchronisation: pour signaler un événement d une tâche vers une autre. pour synchroniser deux tâches dans leur avancement. Synchronisation entre 2 taches Task1() for(;;) Perform operation; Signal Task #2; OSSemPost(sem1) Wait for signal from task #2; OSSemPend(sem2) Continue operation; Task2() for(;;) Wait for signal from task #1; OSSemPend(sem1) Perform operation; Signal Task #1; OSSemPost(sem2)
Fonctions pour les sémaphores Création du séamaphore : OS_EVENT *OSSemCreate(INT16U value); Suppression du sémaphore : OS_EVENT *OSSemDel(OS_EVENT *pevent, INT8U opt, INT8U *err); Incrémente le sémaphore : INT8U OSSemPost(OS_EVENT *pevent); Décrémente le sémaphore : void OSSemPend(OS_EVENT *pevent, INT16U timeout, INT8U *err); // bloquant void OSSemAccept(OS_EVENT *pevent); // non bloquant Renseigne sur l état du sémaphore : INT8U OSSemQuery(OS_EVENT *pevent, OS_SEM_DATA *pdata); Exemple sémaphore (communication entre tache) OS_EVENT *Sem; Void main(void) Sem = OSSemCreate(0); /* Création d un sémaphore*/... Void Task1() While(1) OSSemPend(Sem, 0, &err);/* Acquiert Semaphore */ /*La tache est bloqué tant que le semaphore n est pas acquis! */ /* le paramètre 0 signifie que le time out est infini */. Void Task2() While(1) OSSemPost(Sem); /* Release semaphore */. L exemple 1 montre comment on peut utiliser un sémaphore pour protéger l accès à une fonction non réentrante.
Mutex : Définition Mutex = Sémaphore binaire Permet de gérer l accès exclusif à une ressource partagée (écran, variable, structure, mémoire, etc ). En plus : Permet de gérer l inversion de priorité. Exemple : inversion de priorité 3 taches T1, T2 et T3 partagent la même ressource. T1 à la priorité la plus élevé (10) T2 à une priorité moyenne (15) T3 à la priorité la plus basse (20)
Exemple : inversion de priorité (1) Exemple : inversion de priorité (2)
Inversion de priorité Pour gérer l inversion de priorité : Une priorité doit être réservé pour le mutex. La priorité réservé pour le mutex doit être plus élevé que toutes les taches qui vont utiliser ce même mutex. OSMutex
OSMutexCreate() OSMutexCreate() permet de créer et d initialiser un Mutex. OS_EVENT *OSMutexCreate(INT8U prio, INT8U *err); Exemple : OS_EVENT *DispMutex; void main (void) OSInit(); /* Initialize µc/os-ii */ DispMutex = OSMutexCreate(20, &err); /* Create Display Mutex */ OSStart(); /* Start Multitasking */ OSMutexDel() OSMutexDel() permet de supprimer un Mutex. OS_EVENT *OSMutexDel(OS_EVENT *pevent, INT8U opt, INT8U *err); Exemple : OS_EVENT *DispMutex; void Task (void *pdata) while (1) DispMutex = OSMutexDel(DispMutex, OS_DEL_ALWAYS, &err); if (DispMutex == (OS_EVENT *)0) /* Mutex has been deleted */
OSMutexPend() OSMutexPend() est utilisé quand une tache désire avoir l accès exclusif à une ressource partagée. void OSMutexPend(OS_EVENT *pevent, INT16U timeout, INT8U *err); Exemple : OS_EVENT *DispMutex; void DispTask (void *pdata) for (;;) OSMutexPend(DispMutex, 0, &err);. /* The only way this task continues is if the mutex is available */ OSMutexAccept() OSMutexAccept() permet de savoir si une ressource est disponible. OSMutexAccept() est non-bloquant à l inverse de OSMutexPend(). INT8U OSMutexAccept(OS_EVENT *pevent, INT8U *err); Exemple : OS_EVENT *DispMutex; void Task (void *pdata) INT8U value; for (;;) value = OSMutexAccept(DispMutex, &err); if (value == 1). /* Resource available, process */ else. /* Resource NOT available */
OSMutexPost() OSMutexPost() permet de libérer l accès à une ressource partagée (libère le mutex). Un mutex ne peut être libéré que si il a été demandé au préalable (OSMutexPend et OSMutexAccept) INT8U OSMutexPost(OS_EVENT *pevent); Exemple : OS_EVENT *DispMutex; void TaskX (void *pdata) for (;;) err = OSMutexPost(DispMutex); Exemple (1) On reprend l exemple avec 3 taches T1 (10), T2 (15) et T3 (20). OS_EVENT *DispMutex; void main (void) OSInit(); /* Initialize µc/os-ii */ DispMutex = OSMutexCreate(9, &err); /* Create Display Mutex */ OSStart(); /* Start Multitasking */ void TaskPrio10 (void *pdata) for (;;) /* Application code */ OSMutexPend(DispMutex, 0, &err); /* Access Common resource */ err =OSMutexPost(DispMutex); /* Application code */
void TaskPrio15(void *pdata) for (;;) /* Application code */ OSMutexPend(DispMutex, 0, &err); /* Access Common resource */ err =OSMutexPost(DispMutex); /* Application code */ void TaskPrio20 (void *pdata) for (;;) /* Application code */ OSMutexPend(DispMutex, 0, &err); /* Access Common resource */ err =OSMutexPost(DispMutex); /* Application code */ Exemple (2) Inversion de priorité 1. La tache 3 acquiert le mutex. 2. La tache 1 désiré accéder au mutex. 3. Inversion de priorité. La tache 3 prend la priorité 9 jusqu à ce qu il libère le mutex : OSMutexPost() 4. La tache 1 peut ensuite accéder au mutex.