Toute la vérité sur RTOS. Article # 21. Boîtes aux lettres: introduction et services de base



Les boîtes aux lettres ont été mentionnées dans l'un des articles précédents (# 5). Ils constituent la deuxième méthode de communication inter-tâches signal à signal la plus simple prise en charge par Nucleus SE et offrent un moyen peu coûteux et flexible de transférer des messages simples entre les tâches.

Articles précédents de la série:
Article # 20. Sémaphores: services auxiliaires et structures de données
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.

Utilisation de boîtes aux lettres


Dans Nucleus SE, les boîtes aux lettres sont définies pendant la phase de génération. Une application peut avoir jusqu'à 16 boîtes aux lettres. Si l'application ne possède pas de boîtes aux lettres, le code d'appel de service et les structures de données associées aux boîtes aux lettres ne seront pas inclus dans l'application.

Une boîte aux lettres n'est qu'un endroit pour stocker des données, dont la taille est suffisante pour stocker une variable de type ADDR et dont l'accès sécurisé est contrôlé de manière à ce que plusieurs tâches puissent l'utiliser. Une tâche peut envoyer des données à une boîte aux lettres. Par conséquent, la boîte aux lettres sera pleine et aucune tâche ne pourra lui envoyer de données tant qu'aucune tâche n'aura effectué l'opération de lecture de la boîte aux lettres ou jusqu'à ce que la boîte aux lettres soit vide. Toute tentative d'envoi de données vers une boîte aux lettres pleine ou de lecture d'une boîte aux lettres vide entraînera une erreur ou une pause de tâche, selon les paramètres d'appel API sélectionnés et la configuration de Nucleus SE.

Boîtes aux lettres et files d'attente


Dans certaines implémentations du système d'exploitation, les boîtes aux lettres ne sont pas implémentées, mais comme alternative, il est proposé d'utiliser une file d'attente. Cela semble logique, car une telle file d'attente fournira les mêmes fonctionnalités qu'une boîte aux lettres. Cependant, la file d'attente est une structure de données plus complexe et transporte beaucoup plus de données auxiliaires, de code et a un temps de service plus long.

Dans Nucleus SE, comme dans Nucleus RTOS, vous pouvez sélectionner n'importe lequel de ces types d'objets.
Si votre application possède plusieurs files d'attente et une boîte aux lettres, il est judicieux d'envisager de remplacer la boîte aux lettres par une file d'attente. Cela augmentera légèrement le montant des frais généraux, mais supprimera tout le code API associé aux boîtes aux lettres. Vous pouvez également configurer l'application avec les deux méthodes et comparer la quantité de données et les performances.

Les files d'attente seront discutées dans les prochains articles.

Configurer les boîtes aux lettres


Nombre de boîtes aux lettres


Comme pour la plupart des objets Nucleus SE, la configuration de la boîte aux lettres est principalement définie par les directives #define dans le fichier nuse_config.h . Le paramètre principal est NUSE_MAILBOX_NUMBER , qui détermine le nombre de boîtes aux lettres dans l'application. La valeur par défaut est zéro (c'est-à-dire qu'il n'y a pas de boîtes aux lettres) et peut prendre des valeurs jusqu'à 16. Une valeur incorrecte entraînera une erreur lors de la compilation, qui sera générée en archivant nuse_config_check.h (elle est incluse dans le fichier nuse_config.c et compilée avec elle ), ce qui déclenchera la directive #error .

La sélection d'une valeur différente de zéro est l'activateur principal des boîtes aux lettres. Ce paramètre est utilisé lors de la définition des structures de données et leur taille dépend de sa valeur (plus à ce sujet dans l'article suivant). 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 le fichier nuse_config.h . Pour les boîtes aux lettres, ces directives sont les suivantes:

NUSE_MAILBOX_SEND NUSE_MAILBOX_RECEIVE NUSE_MAILBOX_RESET NUSE_MAILBOX_INFORMATION NUSE_MAILBOX_COUNT 

Par défaut, ils sont définis sur FALSE , désactivant ainsi tous les appels de service et bloquant l'inclusion de code qui les implémente. Pour configurer les boîtes aux lettres dans l'application, vous devez sélectionner les appels d'API nécessaires et les définir sur TRUE .

Voici une section de code du fichier nuse_config.h .

 /* Number of mailboxes in the system - 0-16 */ #define NUSE_MAILBOX_NUMBER 0 /* Service call enablers: */ #define NUSE_MAILBOX_SEND FALSE #define NUSE_MAILBOX_RECEIVE FALSE #define NUSE_MAILBOX_RESET FALSE #define NUSE_MAILBOX_INFORMATION FALSE #define NUSE_MAILBOX_COUNT FALSE 

Si la fonction API de boîte aux lettres est activée et qu'il n'y a pas de boîtes aux lettres dans l'application (à l'exception de NUSE_Mailbox_Count () , qui est toujours activé), une erreur de compilation se produit. 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 service de boîte aux lettres


Nucleus RTOS prend en charge neuf appels de service associés aux boîtes aux lettres et offrent les fonctionnalités suivantes:

  • Envoi de messages à la boîte aux lettres. Nucleus SE est implémenté dans la fonction NUSE_Mailbox_Send () .
  • Lecture d'un message dans une boîte aux lettres. Nucleus SE implémente la fonction NUSE_Mailbox_Receive () .
  • Restauration d'une boîte aux lettres dans un état inutilisé avec la libération de toutes les tâches suspendues (réinitialisation). Dans Nucleus SE, implémenté dans NUSE_Mailbox_Reset () .
  • Fournir des informations sur une boîte aux lettres spécifique. Nucleus SE est implémenté dans NUSE_Mailbox_Information () .
  • Renvoie le nombre de boîtes aux lettres actuellement configurées dans l'application. Dans Nucleus SE, implémenté dans NUSE_Mailbox_Count () .
  • Ajout d'une nouvelle boîte aux lettres (création). Nucleus SE n'est pas implémenté.
  • Supprimer une boîte aux lettres. Nucleus SE n'est pas implémenté.
  • Renvoyez des pointeurs vers toutes les boîtes aux lettres de l'application. Nucleus SE n'est pas implémenté.
  • Envoi d'un message à toutes les tâches suspendues sur la boîte aux lettres (diffusion). Nucleus SE n'est pas implémenté.

Considérez en détail la mise en œuvre de chaque appel de service.

Appels de service de lecture et d'écriture de boîte aux lettres


Les opérations de base qui peuvent être effectuées sur les boîtes aux lettres sont l'écriture et la lecture de données (envoi et réception). Nucleus RTOS et Nucleus SE fournissent deux appels d'API de base pour ces opérations, qui seront décrits ci-dessous.

Écrire dans la boîte aux lettres


L'appel de l'API RTOS Nucleus pour écrire dans la boîte aux lettres est très flexible, ce qui vous permet de suspendre la tâche implicitement ou avec un délai d'expiration si l'opération ne peut pas être terminée immédiatement (par exemple, lorsque vous essayez d'écrire dans une boîte aux lettres complète). Nucleus SE fournit un appel de service similaire, seule la pause de tâche est facultative et le délai d'attente n'est pas implémenté.

Nucleus RTOS propose également un appel de service pour diffuser des données vers une boîte aux lettres; cet appel n'est pas pris en charge dans Nucleus SE et sera décrit dans la section «Appels d'API non réalisés» dans l'article suivant.

Appelez pour écrire dans une boîte aux lettres dans Nucleus RTOS
Prototype d'appel de service:
STATUS NU_Send_To_Mailbox (boîte aux lettres NU_MAILBOX *, message VOID *, suspension UNSIGNED);

Paramètres:
boîte aux lettres - pointeur vers la boîte aux lettres;
message - un pointeur sur le message à envoyer, composé de quatre éléments de type non signé ;
suspend - la spécification de la suspension de la tâche, peut prendre les valeurs NU_NO_SUSPEND , NU_SUSPEND ou valeur de timeout.

Valeur de retour:
NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_MAILBOX - pointeur de boîte aux lettres non valide;
NU_INVALID_POINTER - pointeur nul vers un message ( NULL );
NU_INVALID_SUSPEND - tentative de suspension à partir d'un thread non lié à une tâche;
NU_MAILBOX_FULL - la boîte aux lettres est pleine, mais le type de suspension n'est pas spécifié;
NU_TIMEOUT - la boîte aux lettres est toujours pleine, même après suspension pour la période spécifiée;
NU_MAILBOX_DELETED - la boîte aux lettres a été supprimée alors que la tâche a été suspendue;
NU_MAILBOX_WAS_RESET - la boîte aux lettres a été réinitialisée alors que la tâche a été suspendue.

Appelez pour écrire dans une boîte aux lettres 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_Mailbox_Send (boîte aux lettres NUSE_MAILBOX, message ADDR *, suspension U8);

Paramètres:
boîte aux lettres - index de boîte aux lettres (ID);
message - un pointeur sur le message à envoyer, il s'agit d'une variable de type ADDR ;
suspend - la spécification de la suspension de la tâche, peut être NUSE_NO_SUSPEND ou NUSE_SUSPEND

Valeur de retour:
NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_MAILBOX - index de boîte aux lettres non valide;
NUSE_INVALID_POINTER - pointeur nul vers un message ( NULL );
NUSE_INVALID_SUSPEND - une tentative de suspension à partir d'un thread non lié ou lorsque la fonctionnalité de blocage d'appels API est désactivée;
NUSE_MAILBOX_FULL - la boîte aux lettres est pleine, mais le type de suspension n'est pas spécifié;
NUSE_MAILBOX_WAS_RESET - la boîte aux lettres a été réinitialisée alors que la tâche a été suspendue.

Implémentation d'entrées de boîte aux lettres dans Nucleus SE
La version du code de fonction API NUSE_Mailbox_Send () (après vérification des paramètres) est sélectionnée à l'aide de la compilation conditionnelle, selon que la prise en charge des appels API est activée pour bloquer (suspendre les tâches) ou non. Considérez les deux options.

Si le verrouillage des tâches est désactivé, la logique de cet appel d'API est assez simple et le code ne nécessite aucune explication:

 if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { return_value = NUSE_MAILBOX_FULL; } else /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; return_value = NUSE_SUCCESS; } 

Le message est stocké dans l'élément correspondant NUSE_Mailbox_Data [] et la boîte aux lettres est marquée comme utilisée.

Si le verrouillage des tâches est activé, le code devient plus complexe:

 do { if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { NUSE_Mailbox_Data[mailbox] = *message; NUSE_Mailbox_Status[mailbox] = TRUE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox full */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_FULL; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_SUSPEND); return_value = NUSE_Task_Blocking_Return[NUSE_Task_Active]; if (return_value != NUSE_SUCCESS) { suspend = NUSE_NO_SUSPEND; } } } } while (suspend == NUSE_SUSPEND); 

Certaines explications peuvent être utiles.

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

Si la boîte aux lettres est vide, le message est enregistré et l'état de la boîte aux lettres change pour indiquer qu'elle est pleine. Recherche les tâches suspendues (en attente de lecture) sur cette boîte aux lettres. S'il existe de telles tâches, la première d'entre elles reprend. La variable de suspension est définie sur NUSE_NO_SUSPEND et l'appel d'API se termine et renvoie NUSE_SUCCESS .

Si la boîte aux lettres est pleine et que la suspension est NUSE_NO_SUSPEND , l'appel API renverra NUSE_MAILBOX_FULL . Si la suspension est NUSE_SUSPEND , la tâche s'interrompt. Une fois la fonction terminée (par exemple, lorsque la tâche reprend), si la valeur de retour est NUSE_SUCCESS (ce qui indique que la tâche a été reprise car le message a été lu et non que la boîte aux lettres a été réinitialisée), le cycle démarre depuis le début.

Lire la boîte aux lettres


L'appel de l'utilitaire Nucleus RTOS API pour lire une boîte aux lettres est très flexible et vous permet de suspendre les tâches implicitement ou avec un délai d'attente si l'opération ne peut pas être terminée immédiatement (par exemple, lors de la lecture à partir d'une boîte aux lettres vide). Nucleus SE fournit un service similaire, seule la suspension des tâches est facultative et aucun délai d'attente n'est implémenté.

Appelez pour lire une boîte aux lettres dans Nucleus RTOS
Prototype d'appel de service:
STATUS NU_Receive_From_Mailbox (boîte aux lettres NU_MAILBOX *, message VOID *, suspension UNSIGNED);

Paramètres:
boîte aux lettres - un pointeur vers l'unité de commande de boîte aux lettres fourni par l'utilisateur;
message - un pointeur vers le stockage du message reçu, qui a une taille égale à quatre variables de type non signé ;
suspend - spécification de suspension de tâche, peut prendre les valeurs NUSE_NO_SUSPEND , NUSE_SUSPEND ou valeur de délai d'expiration.

Valeur de retour:
NU_SUCCESS - l'appel s'est terminé avec succès;
NU_INVALID_MAILBOX - pointeur de boîte aux lettres non valide;
NU_INVALID_POINTER - pointeur nul vers un message ( NULL );
NU_INVALID_SUSPEND - tentative de suspension d'un flux incorrect (non associé à la tâche du flux);
NU_MAILBOX_EMPTY - la boîte aux lettres est vide et le type de suspension n'a pas été spécifié;
NU_TIMEOUT - la boîte aux lettres est toujours vide, même après la pause de la tâche pour la valeur de délai d'expiration spécifiée;
NU_MAILBOX_DELETED - la boîte aux lettres a été supprimée alors que la tâche a été suspendue;
NU_MAILBOX_WAS_RESET - la boîte aux lettres a été réinitialisée alors que la tâche a été suspendue.

Appelez pour lire une boîte aux lettres 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_Mailbox_Receive (boîte aux lettres NUSE_MAILBOX, message ADDR *, suspension U8);

Paramètres:
boîte aux lettres - index de boîte aux lettres (ID);
message - un pointeur vers le stockage du message reçu, est une variable de type ADDR ;
suspend - spécification de la suspension de la tâche, peut prendre les valeurs NUSE_NO_SUSPEND ou NUSE_SUSPEND .

Valeur de retour:
NUSE_SUCCESS - l'appel s'est terminé avec succès;
NUSE_INVALID_MAILBOX - index de boîte aux lettres non valide;
NUSE_INDALID_POINTER - pointeur nul vers un message ( NULL );
NUSE_INVALID_SUSPEND - une tentative de suspension à partir d'un flux incorrect ou lorsque les appels d'API sont désactivés pour bloquer les tâches;
NUSE_MAILBOX_EMPTY - la boîte aux lettres est vide et le type de suspension de tâche n'est pas spécifié;
NUSE_MAILBOX_WAS_RESET - la boîte aux lettres a été réinitialisée alors que la tâche a été suspendue.

Implémentation de Mailbox Reader dans Nucleus SE
La version du code de fonction API NUSE_Mailbox_Receive () (après vérification des paramètres) est sélectionnée à l'aide de la compilation conditionnelle, selon que les appels API sont activés pour bloquer (suspendre les tâches) ou non. Nous considérerons les deux options.

Si le verrouillage des tâches est désactivé, la logique de cet appel d'API est assez simple et le code ne nécessite aucune explication:

 if (!NUSE_Mailbox_Status[mailbox]) /* mailbox empty */ { return_value = NUSE_MAILBOX_EMPTY; } else { /* mailbox full */ *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; return_value = NUSE_SUCCESS; } 

Le message est lu à partir de l'élément NUSE_Mailbox_Data [] correspondant et la boîte aux lettres est marquée comme vide.

Si le verrouillage des tâches est activé, le code devient plus complexe:

 do { if (NUSE_Mailbox_Status[mailbox]) /* mailbox full */ { *message = NUSE_Mailbox_Data[mailbox]; NUSE_Mailbox_Status[mailbox] = FALSE; if (NUSE_Mailbox_Blocking_Count[mailbox] != 0) { U8 index; /* check whether a task is blocked */ /* on this mailbox */ NUSE_Mailbox_Blocking_Count[mailbox]--; for (index=0; index<NUSE_TASK_NUMBER; index++) { if ((LONIB(NUSE_Task_Status[index]) == NUSE_MAILBOX_SUSPEND) && (HINIB(NUSE_Task_Status[index]) == mailbox)) { NUSE_Task_Blocking_Return[index] = NUSE_SUCCESS; NUSE_Wake_Task(index); break; } } } return_value = NUSE_SUCCESS; suspend = NUSE_NO_SUSPEND; } else /* mailbox empty */ { if (suspend == NUSE_NO_SUSPEND) { return_value = NUSE_MAILBOX_EMPTY; } else { /* block task */ NUSE_Mailbox_Blocking_Count[mailbox]++; NUSE_Suspend_Task(NUSE_Task_Active, (mailbox << 4) | NUSE_MAILBOX_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 enfermé dans une boucle do ... while , qui s'exécute alors que le paramètre suspend est NUSE_SUSPEND .

Si la boîte aux lettres est pleine, le message stocké est renvoyé et l'état de la boîte aux lettres change pour indiquer qu'elle est vide. Recherche les tâches suspendues (en attente d'enregistrement) sur cette boîte aux lettres. S'il existe de telles tâches, la première d'entre elles reprend. La variable de suspension est définie sur NUSE_NO_SUSPEND et l'appel d'API se termine et renvoie NUSE_SUCCESS .

Si la boîte aux lettres est vide et que le paramètre de suspension est NUSE_NO_SUSPEND , l'appel d'API renvoie NUSE_MAILBOX_EMPTY . Si la suspension est NUSE_SUSPEND , la tâche s'interrompt. Si, à la fin de l'appel, la valeur de retour est NUSE_SUCCESS , ce qui indique que la tâche a été reprise car le message a été envoyé (et non que la boîte aux lettres a été réinitialisée), le cycle recommence depuis le début.

L'article suivant examinera les appels d'API supplémentaires associés aux boîtes aux lettres, ainsi que les structures de données correspondantes.

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


All Articles