Toute la vérité sur RTOS. Article # 19. Sémaphores: introduction et services de base



Les sémaphores ont été mentionnés dans l'un des articles précédents (# 5). Leur tùche principale est de contrÎler l'accÚs aux ressources.

Articles précédents de la série:
Article # 18. Groupes d'indicateurs d'événements: services d'assistance et structures de données
Article # 17. Groupes de drapeaux d'événements: introduction et services de base
Article # 16. Signaux
Article # 15. Partitions de mémoire: services et structures de données
Article # 14. Sections de mémoire: introduction et services de base
Article # 13. Structures de données de tùche et appels d'API non pris en charge
Article # 12. Services pour travailler avec des tĂąches
Article # 11. TĂąches: configuration et introduction Ă  l'API
Article # 10. Scheduler: fonctionnalités avancées et préservation du contexte
Article # 9. Scheduler: implémentation
Article # 8. Nucleus SE: conception interne et déploiement
Article # 7. Nucleus SE: Introduction
Article # 6. Autres services RTOS
Article # 5. Interaction et synchronisation des tĂąches
Article # 4. TĂąches, changement de contexte et interruptions
Article # 3. TĂąches et planification
Article # 2. RTOS: Structure et mode temps réel
Article # 1. RTOS: introduction.

Utiliser des sémaphores


Dans Nucleus SE, les sémaphores sont définis au stade de l'assemblage. Une application peut avoir jusqu'à 16 sémaphores. Si les sémaphores ne sont pas spécifiés, le code des appels de service et les structures de données ne sont pas inclus dans l'application.

Un sémaphore est un compteur de type U8 dont l'accÚs est contrÎlé de maniÚre à ce que plusieurs tùches puissent l'utiliser. Une tùche peut diminuer la valeur du compteur de sémaphores (capture) ou l'augmenter (libération). Tenter de capturer un sémaphore avec une valeur de zéro peut entraßner une erreur ou une suspension de tùche, selon les paramÚtres d'appel d'API sélectionnés et la configuration de Nucleus SE.

Configuration de sémaphores


Nombre de sémaphores


Comme pour la plupart des objets Nucleus SE, le réglage des sémaphores est déterminé par les directives #define dans nuse_config.h . Le paramÚtre principal est NUSE_SEMAPHORE_NUMBER , qui détermine le nombre de sémaphores dans l'application. Par défaut, le paramÚtre est défini sur 0 (les sémaphores ne sont pas utilisés dans l'application) et peut prendre n'importe quelle valeur jusqu'à 16. Une valeur incorrecte entraßnera une erreur de compilation, qui sera générée en archivant nuse_config_check.h (ce fichier est inclus dans nuse_config.c , ce qui signifie qu'il compile avec ce module), par conséquent, la directive #error se déclenchera .

Le choix d'une valeur différente de zéro sert d'activateur principal pour les sémaphores. Ce paramÚtre est utilisé lors de la définition des structures de données et leur taille dépend de sa valeur (pour plus de détails, voir plus loin dans cet article). De plus, une valeur différente de zéro active les paramÚtres de l'API.

Activer les appels API


Chaque fonction API (appel d'utilitaire) dans Nucleus SE est activée par la directive #define dans nuse_config.h . Pour les sémaphores, cela comprend:

NUSE_SEMAPHORE_OBTAIN
NUSE_SEMAPHORE_RELEASE
NUSE_SEMAPHORE_RESET
NUSE_SEMAPHORE_INFORMATION
NUSE_SEMAPHORE_COUNT


Par défaut, ils sont définis sur FALSE , désactivant ainsi chaque appel de service et bloquant l'inclusion de code qui les implémente. Pour configurer des sémaphores, vous devez sélectionner les appels d'API nécessaires et définir les directives correspondantes sur TRUE .

Ce qui suit est un extrait du fichier nuse_config.h par défaut.

 #define NUSE_SEMAPHORE_NUMBER 0 /* Number of semaphores in the system - 0-16 */ #define NUSE_SEMAPHORE_OBTAIN FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RELEASE FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_RESET FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_INFORMATION FALSE /* Service call enabler */ #define NUSE_SEMAPHORE_COUNT FALSE /* Service call enabler */ 

Une fonction API activée s'il n'y a pas de sémaphores dans l'application entraßnera une erreur de compilation (à l'exception de NUSE_Semaphore_Count () , qui est toujours activé). Si votre code utilise un appel d'API qui n'a pas été activé, une erreur de mise en page se produira car le code d'implémentation n'a pas été inclus dans l'application.

Appels de sémaphore utilitaire


Nucleus RTOS prend en charge huit appels de service qui offrent les fonctionnalités suivantes:

  • Capture de sĂ©maphore. Nucleus SE est implĂ©mentĂ© dans la fonction NUSE_Semaphore_Obtain () .
  • RelĂąchez le sĂ©maphore. Dans Nucleus SE, il est implĂ©mentĂ© dans la fonction NUSE_Semaphore_Release () .
  • Retour du sĂ©maphore Ă  un Ă©tat inutilisĂ© avec la libĂ©ration de toutes les tĂąches suspendues (redĂ©marrage). Nucleus SE est implĂ©mentĂ© dans NUSE_Semaphore_Reset () .
  • Fournir des informations sur un sĂ©maphore spĂ©cifique. Nucleus SE est implĂ©mentĂ© dans NUSE_Semaphore_Information () .
  • Renvoie le nombre de sĂ©maphores configurĂ©s dans l'application. Nucleus SE implĂ©mentĂ© dans NUSE_Semaphore_Count () .
  • Ajout d'un nouveau sĂ©maphore Ă  l'application. Nucleus SE n'est pas implĂ©mentĂ©.
  • Suppression du sĂ©maphore de l'application. Nucleus SE n'est pas implĂ©mentĂ©.
  • Retour des pointeurs vers tous les sĂ©maphores. Nucleus SE n'est pas implĂ©mentĂ©.

La mise en Ɠuvre de chaque appel de service est dĂ©crite en dĂ©tail ci-dessous.

Appels utilitaires pour capturer et libérer des sémaphores


Les opĂ©rations fondamentales qui peuvent ĂȘtre effectuĂ©es sur les sĂ©maphores sont la capture et la libĂ©ration (diminution et augmentation de la valeur). Nucleus RTOS et Nucleus SE fournissent deux appels d'API de base pour ces opĂ©rations.

Capture de sémaphore


L'appel de l'utilitaire Nucleus RTOS pour capturer un sĂ©maphore est trĂšs flexible et vous permet de suspendre les tĂąches implicitement ou avec un dĂ©lai d'expiration spĂ©cifique si l'opĂ©ration ne peut pas ĂȘtre effectuĂ©e pour le moment, par exemple, si vous essayez de capturer un sĂ©maphore avec une valeur nulle. Nucleus SE offre les mĂȘmes fonctionnalitĂ©s, seule la pause de tĂąche est facultative et aucun dĂ©lai d'attente n'est implĂ©mentĂ©.

Défi pour capturer un sémaphore dans Nucleus RTOS
Prototype d'appel de service:
STATUS NU_Obtain_Semaphore (sémaphore NU_SEMAPHORE *, suspension UNSIGNED);

ParamĂštres:

sémaphore - pointeur vers le bloc de contrÎle de sémaphore fourni par l'utilisateur;
suspend - paramÚtre de suspension de tùche, peut prendre les valeurs NU_NO_SUSPEND ou NU_SUSPEND , ainsi que la valeur du délai d'expiration.

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succÚs;
NU_UNAVAILABLE - le sémaphore avait une valeur nulle;
NU_INVALID_SEMAPHORE - pointeur invalide vers un sémaphore;
NU_INVALID_SUSPEND - tentative de pause à partir d'un thread non lié à une tùche;
NU_SEMAPHORE_WAS_RESET - le sémaphore a été réinitialisé alors que la tùche était suspendue.

Défi pour capturer un sémaphore dans Nucleus SE
Cet appel d'API prend en charge la fonctionnalité principale de l'API Nucleus RTOS.

Prototype d'appel de service:

STATUS NUSE_Semaphore_Obtain (sémaphore NUSE_SEMAPHORE, suspension U8);

ParamĂštres:

sémaphore - index (ID) du sémaphore utilisé;
suspend - paramĂštre de suspension de tĂąche, peut ĂȘtre NUSE_NO_SUSPEND ou NUSE_SUSPEND .

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succÚs;
NUSE_UNAVAILABLE - le sémaphore avait une valeur nulle;
NUSE_INVALID_SEMAPHORE - index de sémaphore invalide;
NUSE_INVALID_SUSPEND - une tentative de pause à partir d'un thread non lié à une tùche ou lorsque la fonctionnalité de blocage d'API est désactivée;
NUSE_SEMAPHORE_WAS_RESET - le sémaphore a été réinitialisé alors que la tùche a été suspendue;

Implémentation de la capture de sémaphore dans Nucleus SE
La version du code de fonction NUSE_Semaphore_Obtain () (aprÚs vérification des paramÚtres) est sélectionnée à l'aide de la compilation conditionnelle selon que la prise en charge des tùches de blocage (pause) est activée ou non. Considérez les deux options.

Si le verrou n'est pas activé, la logique de cet appel d'API est assez simple:

 if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; } else /* semaphore unavailable */ { return_value = NUSE_UNAVAILABLE; } 

La valeur du compteur de sémaphores est vérifiée et, si elle n'est pas égale à zéro, diminue.

Si le verrouillage des tùches est activé, la logique devient plus complexe:

 do { if (NUSE_Semaphore_Counter[semaphore] != 0) /* semaphore available */ { NUSE_Semaphore_Counter[semaphore]--; return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* semaphore unavailable */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_UNAVAILABLE; } else { /* block task */ NUSE_Semaphore_Blocking_Count[semaphore]++; NUSE_Suspend_Task(NUSE_Task_Active, semaphore << 4) | NUSE_SEMAPHORE_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

Certaines clarifications peuvent ĂȘtre utiles.

Le code est placé dans une boucle do ... while , qui s'exécute alors que le paramÚtre suspend est NUSE_SUSPEND .

Si le sémaphore a une valeur non nulle, il diminue. La variable suspend est définie sur NUSE_NO_SUSPEND et l'appel API se termine et renvoie NUSE_SUCESS .

Si le sémaphore est nul et que la variable de suspension est définie sur NUSE_NO_SUSPEND , l'appel d'API renvoie NUSE_UNAVAILABLE . Si la suspension a été définie sur NUSE_SUSPEND , la tùche s'interrompt. Une fois l'appel terminé (par exemple, lorsque la tùche reprend), si la valeur de retour est NUSE_SUCCESS (qui indique que la tùche a été reprise aprÚs la libération du sémaphore et non aprÚs la réinitialisation), le cycle recommence depuis le début.

Sortie du sémaphore


L'appel d'utilitaire Ă  l'API Nucleus RTOS pour libĂ©rer le sĂ©maphore est assez simple: la valeur du compteur de sĂ©maphore augmente et un message de rĂ©ussite est renvoyĂ©. Nucleus SE offre les mĂȘmes fonctionnalitĂ©s, mais avec une vĂ©rification de dĂ©bordement supplĂ©mentaire.

Défi de publier des sémaphores dans Nucleus RTOS
Prototype d'appel de service:

STATUS NU_Release_Semaphore (NU_SEMAPHORE * sémaphore);

ParamĂštres:

sémaphore - un pointeur vers un bloc de contrÎle de sémaphore fourni par l'utilisateur.

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succÚs;
NU_INVALID_SEMAPHORE - Pointeur de sémaphore non valide .

Défi de publier un sémaphore dans Nucleus SE
Cet appel d'API prend en charge la fonctionnalité principale de l'API Nucleus RTOS.

Prototype d'appel de service:

STATUS NUSE_Semaphore_Release (sémaphore NUSE_SEMAPHORE);

ParamĂštres:

sémaphore - L'index (ID) du sémaphore libéré.

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succÚs;
NUSE_INVALID_SEMAPHORE - index de sémaphore invalide;
NUSE_UNAVAILABLE - le sĂ©maphore a une valeur de 255 et ne peut pas ĂȘtre augmentĂ©.

Implémentation d'une version de sémaphore dans Nucleus SE
Le code de fonction NUSE_Semaphore_Release () (aprÚs vérification des paramÚtres) est commun, que le verrouillage des tùches soit activé ou non. La valeur du compteur de sémaphores est vérifiée et si elle est inférieure à 255, elle augmente.

Un code supplémentaire est sélectionné à l'aide de la compilation conditionnelle si la prise en charge des appels de blocage d'API (suspension de tùche) est activée:

 NUSE_CS_Enter(); if (NUSE_Semaphore_Counter[semaphore] < 255) { NUSE_Semaphore_Counter[semaphore]++; return_value = NUSE_SUCCESS; #if NUSE_BLOCKING_ENABLE if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether a task is blocked */ /* on this semaphore */ NUSE_Semaphore_Blocking_Count[semaphore]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } #endif } else { return_value = NUSE_UNAVAILABLE; } NUSE_CS_Exit(); return return_value; 

Si des tùches sont suspendues sur ce sémaphore, la premiÚre d'entre elles reprend.

L'article suivant décrit les appels d'API supplémentaires associés aux sémaphores et à leurs structures de données.

À propos de l'auteur: Colin Walls travaille dans l'industrie Ă©lectronique depuis plus de trente ans, consacrant la majeure partie de son temps au micrologiciel. Il est maintenant ingĂ©nieur firmware chez Mentor Embedded (une division de Mentor Graphics). Colin Walls intervient souvent lors de confĂ©rences et sĂ©minaires, auteur de nombreux articles techniques et de deux livres sur le firmware. Vit au Royaume-Uni. Blog professionnel de Colin , e-mail: colin_walls@mentor.com.

Source: https://habr.com/ru/post/fr429156/


All Articles