
Dans des articles précédents, nous avons examiné le modèle multitâche et découvert que chaque tâche est un programme quasi indépendant. Bien que les tâches dans les systèmes embarqués aient un certain degré d'indépendance, cela ne signifie pas qu'elles ne se «connaissent» pas. Certaines tâches seront vraiment isolées des autres, mais l'interaction et la synchronisation entre elles sont une exigence courante. Ce mécanisme est l'une des fonctions clés du RTOS. La gamme de fonctions peut varier en fonction du RTOS, par conséquent, dans cet article, nous considérerons les options disponibles publiquement.
Articles précédents de la série:
Article # 4. Tâches, changement de contexte et interruptionsArticle # 3. Tâches et planificationArticle # 2. RTOS: Structure et mode temps réel
Article # 1. RTOS: introduction.
Gamme de fonctions
Il existe trois modèles d'interaction et de synchronisation entre les tâches:
- Les services sont liés aux tâches: RTOS fournit aux tâches des attributs qui assurent une interaction entre elles. Considérez les signaux comme exemple.
- Les objets du noyau sont des moyens de communication ou de synchronisation autonomes. Exemples: drapeaux d'événements, boîtes aux lettres, files d'attente / canaux, sémaphores et mutex.
- La messagerie est un schéma simplifié dans lequel le RTOS vous permet de créer des objets de message et de les transférer d'une tâche à une autre ou à plusieurs. Ceci est fondamental pour l'architecture du noyau, et donc un tel système est appelé "RTOS de messagerie".
Les mécanismes idéaux pour différents processus varieront. Leurs capacités peuvent se chevaucher, il vaut donc la peine d'envisager l'évolutivité de ces modèles. Par exemple, si une application nécessite plusieurs files d'attente, mais une seule boîte aux lettres, vous pouvez implémenter une boîte aux lettres avec une file d'attente pour un élément. Cet objet ne sera pas complètement optimal, mais le code entier de la boîte aux lettres ne sera pas inclus dans l'application et, par conséquent, l'évolutivité réduira la quantité de mémoire utilisée par le RTOS.
Variables communes ou zones de mémoire
Une approche simplifiée de l'interaction entre les tâches est la présence dans le système de variables ou de zones de mémoire disponibles pour toutes les tâches. Cette approche peut être appliquée à plusieurs processus, malgré sa simplicité. L'accès doit être contrôlé. Si la variable n'est qu'un octet, l'écriture ou la lecture est probablement une opération atomique (c'est-à -dire continue), mais il faut faire attention si le processeur autorise d'autres opérations sur les octets de la mémoire, car elles peuvent être interruptibles et peuvent se produire problème de synchronisation. Une façon de mettre en œuvre le verrouillage / déverrouillage consiste à désactiver les interruptions pendant une courte période.
Si vous utilisez une zone mémoire, vous avez toujours besoin d'un verrou. Vous pouvez utiliser le premier octet comme indicateur de blocage, étant donné que l'architecture de la mémoire fournit un accès atomique à cet octet. Une tâche charge les données dans une zone de mémoire, définit un indicateur, puis attend qu'elle soit réinitialisée. Une autre tâche consiste à attendre la définition de l'indicateur, à lire les données et à réinitialiser l'indicateur. Il est moins judicieux d'utiliser la désactivation d'interruption comme verrou car le déplacement de la totalité du tampon de données peut prendre un certain temps.
Cette utilisation de la mémoire partagée est similaire à la mise en œuvre de nombreuses communications interprocesseurs dans des systèmes multicœurs. Dans certains cas, le verrouillage et / ou l'interruption du matériel sont intégrés à l'interface interprocesseur de la mémoire partagée.
Signaux
Les signaux sont l'un des mécanismes d'interaction entre les tâches les plus simples offerts par les RTOS traditionnels. Ils contiennent un ensemble d'indicateurs de bits (8, 16 ou 32, selon l'application spécifique), qui est associé à une tâche spécifique.
L'indicateur de signal (ou plusieurs indicateurs) peut être défini par n'importe quelle tâche en utilisant l'opération logique "OU". Les indicateurs ne peuvent être lus que par une tâche contenant un signal. Le processus de lecture est généralement destructeur, c'est-à -dire que les drapeaux sont également réinitialisés.
Dans certains systèmes, les signaux sont implémentés d'une manière plus complexe, de sorte qu'une fonction spéciale attribuée par le propriétaire de la tâche du signal est automatiquement exécutée lorsque des indicateurs de signal sont définis. Cela élimine la nécessité pour la tâche de contrôler les indicateurs lui-même. Ceci est quelque peu similaire à un gestionnaire d'interruption.
Groupes de drapeaux d'événements
Les groupes d'indicateurs d'événements sont similaires aux signaux en ce sens qu'ils sont un outil orienté bits pour l'interaction entre les tâches. De même, ils peuvent contenir 8, 16 ou 32 bits. Contrairement aux signaux, ce sont des objets centraux indépendants et «n'appartiennent» à aucune tâche particulière. N'importe quelle tâche peut définir et réinitialiser des drapeaux d'événements à l'aide des opérations logiques «OU» et «ET». De même, toute tâche peut vérifier les indicateurs d'événement en utilisant les mêmes opérations. Dans de nombreux RTOS, vous pouvez effectuer un appel d'API de blocage pour une combinaison de drapeaux d'événements. En d'autres termes, la tâche peut être suspendue jusqu'à ce qu'une combinaison spécifique d'indicateurs d'événement soit définie. L'option «consommer» peut également être disponible lors de la vérification des drapeaux d'événements, ce qui réinitialise tous les drapeaux.
Sémaphores
Les sémaphores sont des objets noyau indépendants utilisés pour la comptabilité des ressources. Il existe deux types de sémaphores: binaire (ne peut avoir que deux valeurs) et général (nombre illimité de valeurs). Certains processeurs prennent en charge les instructions (atomiques) qui facilitent la mise en œuvre rapide des sémaphores binaires. Les sémaphores binaires peuvent être implémentés en tant que sémaphores généraux avec une valeur de 1.
N'importe quelle tâche peut essayer d'attribuer un sémaphore pour accéder à la ressource. Si la valeur actuelle du sémaphore est supérieure à 0 (le sémaphore est libre), la valeur du compteur est réduite de 1, par conséquent, l'affectation réussit. Dans de nombreux systèmes d'exploitation, un mécanisme de verrouillage peut être utilisé pour attribuer un sémaphore. Cela signifie que la tâche peut être dans un état d'attente jusqu'à ce que le sémaphore soit libéré par une autre tâche. N'importe quelle tâche peut libérer le sémaphore, puis la valeur du sémaphore augmentera.
Boîtes aux lettres
Les boîtes aux lettres sont des objets du noyau indépendants qui permettent d'envoyer des messages. La taille du message dépend de l'implémentation, mais elle est généralement fixe. Les tailles de message typiques sont de un à quatre éléments de la taille d'un pointeur. En règle générale, un pointeur vers des données plus complexes est envoyé via la boîte aux lettres. Certains noyaux implémentent des boîtes aux lettres de telle manière que les données sont simplement stockées dans une variable régulière et que le noyau contrôle l'accès à celles-ci. Les boîtes aux lettres peuvent également être appelées «échange», bien que ce nom soit maintenant rarement vu.
Toute tâche peut envoyer des messages à une boîte aux lettres, qui est ensuite remplie. Si une tâche tente d'envoyer un message à une boîte aux lettres pleine, elle recevra une réponse d'erreur. Dans de nombreux RTOS, vous pouvez utiliser un mécanisme de blocage pour envoyer à la boîte aux lettres. Cela signifie que la tâche sera suspendue jusqu'à ce que le message dans la boîte aux lettres soit lu. Toute tâche peut lire les messages de la boîte aux lettres, après quoi elle est vide. Si une tâche tente de lire à partir d'une boîte aux lettres vide, elle recevra une réponse d'erreur. Dans de nombreux RTOS, vous pouvez utiliser un appel de blocage pour lire à partir d'une boîte aux lettres. Cela signifie que la tâche sera suspendue jusqu'à ce qu'un nouveau message apparaisse dans la boîte aux lettres.
Certains RTOS prennent en charge la fonction «diffusion». Cela vous permet d'envoyer des messages à toutes les tâches qui sont actuellement suspendues lors de la lecture d'une boîte aux lettres spécifique.
Certains RTOS ne prennent pas du tout en charge les boîtes aux lettres. Au lieu de cela, il est recommandé d'utiliser une file d'attente à élément unique. Ceci est fonctionnellement équivalent, mais entraîne une surcharge supplémentaire pour la mémoire et l'exécution.
Files d'attente
Les files d'attente sont des objets de noyau indépendants qui fournissent un mécanisme d'envoi de messages. Ils sont légèrement plus flexibles et complexes que les boîtes aux lettres. La taille du message dépend de l'implémentation, mais il est généralement fixe et orienté vers le mot / pointeur.
N'importe quelle tâche peut envoyer des messages à la file d'attente, et cela peut être répété jusqu'à ce que la file d'attente soit pleine, après quoi toute tentative d'envoi entraînera une erreur. La longueur de la file d'attente est généralement déterminée par l'utilisateur lors de la création ou de la configuration du système. Dans de nombreux RTOS, vous pouvez appliquer un mécanisme de blocage à la file d'attente. Autrement dit, si la file d'attente est pleine, la tâche peut être suspendue jusqu'à ce que le message dans la file d'attente soit lu par une autre tâche. Toute tâche peut lire les messages de la file d'attente. Les messages sont lus dans l'ordre dans lequel ils ont été envoyés (premier entré - premier sorti, FIFO). Si une tâche essaie de lire à partir d'une file d'attente vide, elle recevra une réponse d'erreur. Dans de nombreux RTOS, un mécanisme de blocage peut être utilisé pour lire à partir d'une file d'attente vide. Autrement dit, si la file d'attente est vide, la tâche peut être suspendue jusqu'à ce que le message soit envoyé à la file d'attente par une autre tâche.
Très probablement, il y aura un mécanisme dans le RTOS pour envoyer un message à l'avant de la file d'attente, c'est ce qu'on appelle le brouillage. Certains RTOS prennent également en charge la fonction de diffusion. Cela vous permet d'envoyer des messages à toutes les tâches suspendues lors de la lecture de la file d'attente.
De plus, le RTOS peut prendre en charge l'envoi et la lecture de messages de longueur variable. Cela donne plus de flexibilité, mais entraîne des frais supplémentaires.
De nombreux RTOS prennent en charge un autre type d'objet noyau, les «tuyaux». En substance, un canal est similaire à une file d'attente, mais traite les données orientées octets.
La fonctionnalité des files d'attente n'est pas intéressante, mais il faut comprendre qu'elles ont plus de surcharge pour la mémoire et l'exécution que les boîtes aux lettres, principalement parce qu'il est nécessaire de sauvegarder deux pointeurs: le début et la fin de la file d'attente.
Mutex
Les mutex (sémaphores mutuellement exclusifs) sont des objets noyaux indépendants qui se comportent très bien comme des sémaphores binaires normaux. Ils sont un peu plus compliqués que les sémaphores et incluent le concept de propriété temporaire (une ressource dont l'accès est contrôlé). Si une tâche attribue un mutex, seule la même tâche peut le libérer à nouveau: le mutex (et, par conséquent, la ressource) appartient temporairement à la tâche.
Les mutex ne sont pas fournis par tous les RTOS, mais le sémaphore binaire régulier est assez facile à adapter. Vous devez écrire une fonction d'obtention mutex qui affecte un sémaphore et attribue un identificateur de tâche. Ensuite, la fonction supplémentaire «mutex release» vérifie l'identifiant de la tâche appelante et ne libère le sémaphore que s'il correspond à la valeur stockée, sinon il retournera une erreur.
Lorsque nous avons travaillé sur notre propre système d'exploitation OSRV MAX en temps réel (articles déjà publiés à ce sujet), notre équipe a «découvert» le blog de Colin Walls, un expert en microélectronique et firmware de Mentor Graphics. Les articles semblaient intéressants, les traduisaient eux-mêmes, mais afin de ne pas "écrire sur la table", ils ont décidé de publier. J'espère qu'ils vous seront également utiles. Si c'est le cas, nous prévoyons de publier tous les articles traduits de la série.
À 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: blogs.mentor.com/colinwalls , e-mail: colin_walls@mentor.comLisez les
premier, deuxième, troisième et quatrième articles publiés plus tôt.