Toute la vérité sur RTOS. Article # 20. Sémaphores: services auxiliaires et structures de données



Cet article poursuit l'examen des sémaphores.

Services de sémaphore auxiliaires


Nucleus RTOS dispose de quatre appels d'API qui fournissent des fonctionnalités liées aux sémaphores: réinitialisation d'un sémaphore, récupération des informations de sémaphore, récupération du nombre de sémaphores dans une application et récupération de pointeurs vers tous les sémaphores d'une application. Les trois premiers d'entre eux sont implémentés dans Nucleus SE.

Articles précédents de la série:
Article # 19. SĂ©maphores: introduction et services de base
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.

Réinitialiser le sémaphore


Cet appel d'API réinitialise le sémaphore à son état initial non utilisé. Cette fonction d'API est inhabituelle par rapport aux fonctions d'autres objets du noyau, car malgré le fait qu'elle effectue une réinitialisation, elle définit non seulement le compteur à la valeur initiale, mais une nouvelle valeur de compteur initiale est transmise dans l'appel. Toute tâche qui a été interrompue sur le sémaphore reprend et renvoie le code NUSE_SEMAPHORE_WAS_RESET dans Nucleus SE et dans Nucleus RTOS, NU_SEMAPHORE_RESET .

Appel pour réinitialiser le sémaphore dans Nucleus RTOS

Prototype d'appel de service:

STATUS NU_Reset_Semaphore (NU_SEMAPHORE * sémaphore, UNSIGNED initial_count);

Paramètres:

sémaphore - pointeur vers le bloc de contrôle de sémaphore fourni par l'utilisateur;
initial_count - la valeur à laquelle le sémaphore sera défini.

Valeur de retour:

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

Appel pour réinitialiser 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_Reset (sémaphore NUSE_SEMAPHORE, U8 initial_count);

Paramètres:

sémaphore - index (ID) du sémaphore vidé;
initial_count - la valeur à laquelle le sémaphore sera défini.

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_SEMAPHORE - index de sémaphore invalide.

Implémentation d'une réinitialisation de sémaphore dans Nucleus SE

La tâche principale de la fonction API NUSE_Semaphore_Reset () consiste à définir l'élément NUSE_Semaphore_Counter [] correspondant à la valeur spécifiée (après vérification des paramètres).

Si le verrouillage des tâches est activé, le code suivant est requis pour déverrouiller les tâches:

while (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; /* check whether any tasks are blocked */ /* on this 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_SEMAPHORE_WAS_RESET; NUSE_Task_Status[index] = NUSE_READY; break; } } NUSE_Semaphore_Blocking_Count[semaphore]--; } #if NUSE_SCHEDULER_TYPE == NUSE_PRIORITY_SCHEDULER NUSE_Reschedule(NUSE_NO_TASK); #endif 

Chaque tâche suspendue sur le sémaphore est marquée comme «terminée» et le code de suspension de tâche renvoie NUSE_SEMAPHORE_WAS_RESET . Une fois ce processus terminé, si le planificateur de priorité est utilisé, l'appel initialise NUSE_Reschedule () , car une ou plusieurs tâches avec une priorité plus élevée peuvent passer à l'état prêt et attendent de reprendre.

Informations sur le sémaphore


Cet appel d'utilitaire renvoie des informations sur le sémaphore. L'implémentation de cet appel dans Nucleus SE diffère de Nucleus RTOS en ce que moins d'informations sont renvoyées, car le nommage des objets et l'ordre de la suspension ne sont pas pris en charge, et la suspension des tâches elle-même peut être désactivée.

Appel d'informations sur les sémaphores dans Nucleus RTOS

Prototype d'appel de service:

STATUS NU_Semaphore_Information (NU_SEMAPHORE * sémaphore, nom CHAR *, UNSIGNED * current_count, OPTION * suspend_type, UNSIGNED * tasks_waiting, NU_TASK ** first_task);

Paramètres:

sémaphore - un pointeur vers le bloc de contrôle de sémaphore sur lequel les informations sont requises;
nom - pointeur sur le nom à 8 caractères du sémaphore, avec zéro octet de fin inclus dans cette zone;
current_count - un pointeur sur une variable qui prendra la valeur actuelle du compteur de sémaphore;
suspend_type - un pointeur vers une variable qui acceptera le type de tâche suspendue, peut prendre les valeurs NU_FIFO et NU_PRIORITY ;
task_waiting - un pointeur sur une variable qui prendra le nombre de tâches suspendues dans le sémaphore;
first_task - un pointeur vers une variable de type NU_TASK , qui prendra un pointeur vers l'unité de contrôle de la première tâche suspendue.

Valeur de retour:

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

Appel d'informations sur les sémaphores 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_Information (sémaphore NUSE_SEMAPHORE, U8 * current_count, U8 * tasks_waiting, NUSE_TASK * first_task);

Paramètres:

sémaphore - un index d'un sémaphore sur lequel il est nécessaire de fournir des informations;
current_count - un pointeur sur une variable qui prendra la valeur actuelle du compteur de sémaphore;
tasks_waiting - un pointeur sur une variable qui prendra le nombre de tâches suspendues sur ce sémaphore (rien n'est retourné si la prise en charge de la suspension des tâches est désactivée);
first_task - un pointeur vers une variable de type NUSE_TASK , qui prendra l'index de la première tâche suspendue (rien n'est retourné si la prise en charge des tâches de pause est désactivée)

Valeur de retour:

NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_SEMAPHORE - index de sémaphore invalide;
NUSE_INVALID_POINTER - un ou plusieurs paramètres de pointeur sont incorrects.

Implémentation d'informations sur les sémaphores dans Nucleus SE

L'implémentation de cet appel d'API est assez simple:

 NUSE_CS_Enter(); *current_count = NUSE_Semaphore_Counter[semaphore]; #if NUSE_BLOCKING_ENABLE *tasks_waiting = NUSE_Semaphore_Blocking_Count[semaphore]; if (NUSE_Semaphore_Blocking_Count[semaphore] != 0) { U8 index; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_SEMAPHORE_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == semaphore)) { *first_task = index; break; } } } else { *first_task = 0; } #else *tasks_waiting = 0; *first_task = 0; #endif NUSE_CS_Exit(); return NUSE_SUCCESS; 

La fonction renvoie l'état du sémaphore. Ensuite, si la fonctionnalité de blocage d'appels API est activée, le nombre de tâches en attente et l'index de la première sont renvoyés (sinon, ces paramètres sont définis sur 0).

Obtenir le nombre de sémaphores


Cet appel d'utilitaire renvoie le nombre de sémaphores dans l'application. Dans Nucleus RTOS, cette valeur change au fil du temps et la valeur de retour correspond au nombre actuel de sémaphores, et dans Nucleus SE, la valeur de retour est définie au stade de l'assemblage et ne change plus.

Appel d'un compteur de sémaphores dans Nucleus RTOS

Prototype d'appel de service:

UNSIGNED NU_Established_Semaphores (VOID);

Paramètres:
Sont absents.

Valeur de retour:
Le nombre de sémaphores créés dans l'application.

Appel d'un compteur de sémaphores dans Nucleus SE
Cet appel d'API prend en charge la fonctionnalité principale de l'API Nucleus RTOS.

Prototype d'appel de service:

U8 NUSE_Semaphore_Count (void);

Paramètres:
Sont absents.

Valeur de retour:
Le nombre de sémaphores configurés dans l'application.

Implémentation de compteurs de sémaphores dans Nucleus SE
L'implémentation de cet appel API est assez simple: la valeur du symbole #define NUSE_SEMAPHORE_NUMBER est retournée .

Structures de données


Les sémaphores utilisent deux ou trois tableaux de structures de données (en RAM et ROM), qui, comme tous les autres objets Nucleus SE, sont un ensemble de tables dont la taille dépend du nombre de sémaphores dans l'application et des paramètres sélectionnés.

Je recommande fortement que le code d'application n'utilise pas un accès direct à ces structures de données, mais s'y réfère via les fonctions API fournies. Cela évitera l'incompatibilité avec les futures versions de Nucleus SE et les effets secondaires indésirables, ainsi que simplifiera le portage de l'application vers Nucleus RTOS. Pour une meilleure compréhension du fonctionnement du code d'appel de service et pour le débogage, un aperçu détaillé des structures de données est donné ci-dessous.

Données RAM


Ces données ont la structure suivante:
NUSE_Semaphore_Counter [] - un tableau de type U8 qui a une entrée pour chaque sémaphore configuré, il stocke la valeur du compteur.
NUSE_Semaphore_Blocking_Count [] - un tableau de type U8 , contient des compteurs de tâches bloquées sur chaque sémaphore. Ce tableau existe uniquement si la fonctionnalité de blocage d'appels API est activée.
NUSE_Semaphore_Counter [] est initialisé à la valeur initiale (voir "Données dans la ROM" ci-dessous) et NUSE_Semaphore_Blocking_Count [] est réinitialisé à l' aide de NUSE_Init_Semaphore () au démarrage de Nucleus SE. L'un des articles suivants fournira une description complète des procédures de démarrage de Nucleus SE.

Voici les définitions de ces structures de données dans le fichier nuse_init.c .

 RAM U8 NUSE_Semaphore_Counter[NUSE_SEMAPHORE_NUMBER]; #if NUSE_BLOCKING_ENABLE RAM U8 NUSE_Semaphore_Blocking_Count[NUSE_SEMAPHORE_NUMBER]; #endif 

Données ROM


Structure des données:
NUSE_Semaphore_Initial_Value [] - un tableau de type U8 , ayant un enregistrement pour chaque sémaphore, ce sont les valeurs initiales des sémaphores.

Cette structure de données est déclarée et initialisée (statiquement) dans nuse_config.c :

 ROM U8 NUSE_Semaphore_Initial_Value[NUSE_SEMAPHORE_NUMBER] = { /* semaphore initial count values */ }; 

La quantité de mémoire pour les sémaphores


Comme tous les objets du noyau Nucleus SE, la quantité de données nécessaires pour les sémaphores est prévisible.

La quantité de mémoire en ROM (en octets) pour tous les sémaphores de l'application est NUSE_SEMAPHORE_NUMBER .

La quantité de mémoire en RAM (en octets) pour tous les sémaphores dans l'application avec des appels activés à l'API de verrouillage peut être calculée comme suit:
NUSE_SEMAPHORE_NUMBER * 2

Sinon, c'est NUSE_SEMAPHORE_NUMBER .

Appels d'API non réalisés


Trois appels d'API pour les sémaphores présents dans Nucleus RTOS ne sont pas implémentés dans Nucleus SE.

Création de sémaphores


Cet appel API crée un sémaphore. Il n'y a pas besoin de Nucleus SE car les sémaphores sont créés statiquement.

Prototype d'appel de service:
STATUS NU_Create_Semaphore (NU_SEMAPHORE * sémaphore, nom CHAR *, UNSIGNED initial_count, OPTION suspend_type);

Paramètres:

sémaphore - un pointeur vers un bloc de contrôle de sémaphore fourni par l'utilisateur; il est utilisé pour contrôler les sémaphores dans d'autres appels d'API;
nom - un pointeur sur le nom du sémaphore à 8 caractères, avec l'octet nul de fin activé;
initial_count - la valeur initiale du sémaphore;
suspend_type - indique le principe de la pause d'une tâche sur un sémaphore. Il peut prendre les valeurs NU_FIFO et NU_PRIORITY , correspondant au principe de FIFO (First-in-First-Out) et l'ordre de priorité de la suspension des tâches.

Valeur de retour:

NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_SEMAPHORE - Dit que le pointeur vers le bloc de contrôle du sémaphore est NULL ou est déjà utilisé;
NU_INVALID_SUSPEND - paramètre suspend_type non valide .

Suppression du sémaphore


Cet appel d'API supprime le sémaphore créé précédemment. Nucleus SE n'est pas nécessaire car les sémaphores sont créés statiquement et ne peuvent pas être supprimés.

Prototype d'appel de service:

STATUS NU_Delete_Semaphore (NU_SEMAPHORE * semaphore);

Paramètres:

sémaphore - pointeur vers le bloc de contrôle du sémaphore.

Valeur de retour:

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

Pointeurs vers les sémaphores


Cet appel API forme une liste séquentielle de pointeurs vers tous les sémaphores du système. Il n'a pas besoin de Nucleus SE, car les sémaphores sont identifiés par un simple index, pas un pointeur.

Prototype d'appel de service:

UNSIGNED NU_Semaphore_Pointers (NU_SEMAPHORE ** pointer_list, UNSIGNED maximum_pointers);

Paramètres:

pointer_list - un pointeur vers un tableau de pointeurs NU_SEMAPHORE , ce tableau est rempli de pointeurs vers des sémaphores;
maximum_pointers - le nombre maximum de pointeurs dans le tableau.

Valeur de retour:
Le nombre de pointeurs NU_SEMAPHORE dans le tableau.

Compatible avec Nucleus RTOS


Comme pour tous les autres objets Nucleus SE, l'objectif était de maximiser la compatibilité du code d'application avec Nucleus RTOS. Les sémaphores ne font pas exception et, du point de vue de l'utilisateur, ils sont implémentés de la même manière que dans Nucleus RTOS. Il y a aussi une certaine incompatibilité, que j'ai jugée acceptable étant donné que le code final deviendra plus compréhensible et plus efficace en termes de quantité de mémoire requise. Sinon, les appels d'API Nucleus RTOS peuvent être utilisés presque directement comme appels Nucleus SE.

Identificateurs d'objet


Dans Nucleus RTOS, tous les objets sont décrits par des structures de données (unités de contrôle) d'un type spécifique. Un pointeur vers cette unité de contrôle sert d'identifiant pour le sémaphore. J'ai décidé que dans Nucleus SE, une approche différente est nécessaire pour une utilisation efficace de la mémoire: tous les objets du noyau sont décrits par un ensemble de tables en RAM et / ou ROM. La taille de ces tables est déterminée par le nombre d'objets configurés de chaque type. L'identifiant d'un objet particulier est l'index dans ce tableau. J'ai donc défini NUSE_SEMAPHORE comme l'équivalent de U8 , une variable (pas un pointeur) de ce type sert d'identifiant pour le sémaphore. Cette légère incompatibilité est facile à gérer si le code est porté de Nucleus SE vers Nucleus RTOS et vice versa. En règle générale, aucune opération n'est effectuée sur les identificateurs d'objet autres que le déplacement et le stockage.

Nucleus RTOS prend également en charge les noms de sémaphores. Ces noms sont utilisés uniquement pour le débogage. Je les ai exclus de Nucleus SE pour économiser de la mémoire.

Taille du comptoir


Sur Nucleus RTOS, le compteur de sémaphores est de type unsigned , qui est généralement une variable 32 bits. Nucleus SE a un compteur 8 bits, mais cela peut être facilement changé. Normalement, Nucleus RTOS ne vérifie pas le débordement du sémaphore. L'appel à l'API Nucleus SE ne permettra pas d'attribuer au compteur des valeurs supérieures à 255.

Appels d'API non réalisés


Nucleus RTOS prend en charge huit appels d'utilitaire pour travailler avec des sémaphores. Parmi ceux-ci, trois ne sont pas implémentés dans Nucleus SE. Les détails de ces défis, ainsi que la décision de les exclure de Nucleus SE, ont été décrits ci-dessus.

L'article suivant examine les boîtes aux lettres.

À 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/fr429588/


All Articles