Remplacement des files d'attente dans RTOS


12

Pour la communication entre tâches ou pour partager des données entre deux tâches de RTOS, nous utilisons des files d'attente. Mais le problème avec les files d'attente est qu'elles sont lentes .... Elles copient les données dans le tampon, puis la gestion Mutex et ensuite le transfert de données. C'est énormément lent si vous devez transférer des données volumineuses. Un autre problème est si la même file d'attente est accessible par plusieurs tâches. L'image devient alors la suivante: - Attendez d'abord pour accéder à la file d'attente puis à la gestion interne des mutex de la file d'attente, puis au transfert de données.

Cela augmente les frais généraux sur le système. Quel pourrait être le remplacement efficace des files d'attente?

(Je suppose que cette question est indépendante du RTOS que nous utilisons. La plupart des RTOS gèrent les files d'attente de cette manière uniquement)


Qu'entendez-vous par une file d'attente accessible par plusieurs tâches? Voulez-vous dire publier dans la file d'attente ou lire dans la file d'attente? Plusieurs tâches doivent pouvoir être publiées dans une file d'attente avec un minimum de frais généraux. Le RTOS doit gérer le mutexage afin qu'un poste soit une opération atomique. Pour 99% des tâches, vous devriez avoir une boucle qui attend dans une file d'attente et traite le message. Une file d'attente ne doit (généralement) être lue que par une seule tâche. Vous devrez probablement examiner votre conception et la façon dont vous utilisez les files d'attente au lieu de les remplacer.
Erik

@Erik: Désolé! J'utilise le mécanisme que vous avez mentionné .... Je voulais dire autre chose et j'ai écrit différent .... Je vais le modifier !! Merci d'avoir signalé l'erreur! J'attends l'accès à la file d'attente dans mon code!
Swanand

Réponses:


7

Les files d'attente fonctionnent de cette façon car il s'agit d'un modèle de transaction thread-safe pour la communication entre les tâches. Vous risquez des problèmes de corruption et / ou de propriété des données dans tout schéma moins strict.

Copiez-vous les données dans un tampon en mémoire puis passez-vous un pointeur avec les éléments de file d'attente, ou essayez-vous de passer toutes les données dans les éléments de file d'attente eux-mêmes? Si vous ne passez pas de pointeurs, vous obtiendrez une augmentation des performances en faisant cela au lieu de passer un octet à la fois dans les éléments de file d'attente.


2
J'allais dire la même chose. Si vous passez simplement des pointeurs vers les données dans les files d'attente, vous pouvez augmenter la vitesse, mais assurez-vous de ne pas vous retrouver avec deux threads essayant d'utiliser et de modifier les données.
Kortuk

Comme l'a dit @Kortuk, j'ai besoin de "vous assurer de ne pas vous retrouver avec deux threads essayant d'utiliser et de modifier les données" ... Ce qui signifie une augmentation des frais généraux ... Je ne veux pas beaucoup de traitement! :(
Swanand

Il n'y a donc pas de remplacement en tant que tel pour les files d'attente ... Au lieu de la file d'attente de données, j'ai besoin d'utiliser la file d'attente de pointeur!
Swanand

1
@Swanand si vous planifiez votre application de telle sorte que les files d'attente ne soient que unidirectionnelles (c'est-à-dire que vous ne lisez jamais la même file d'attente en deux tâches) et que vous traitez les données stockées sur le pointeur immédiatement puis les libérez, vous ne devriez pas avoir de problème avec le partage des données. Les frais généraux augmenteront car vous devrez peut-être créer plusieurs files d'attente pour transmettre des données de manière fiable dans les deux sens, mais c'est le coût de faire des affaires dans un environnement multitâche.
AngryEE

7

Un moyen simple consiste à placer un pointeur sur les données dans la file d'attente et à consommer les données à l'aide du pointeur.

Notez que vous échangez la sécurité contre des performances de cette façon car vous devez vous assurer que:

  1. le tampon reste valide jusqu'à ce que le consommateur ait consommé les données
  2. quelqu'un désalloue le tampon

Si vous n'utilisez pas de mémoire allouée dynamiquement, vous n'avez pas à la désallouer, mais vous devez toujours vous assurer que la zone de mémoire n'est pas réutilisée avant que les données aient été consommées.


6

Des files d'attente sans verrouillage peuvent être implémentées pour le cas d'un seul producteur / consommateur unique, et vous pouvez souvent concevoir votre logiciel pour minimiser le nombre de files d'attente multi-producteurs ou multi-consommateurs.

Une file d'attente sans verrou peut être construite comme suit: Allouez un tableau des éléments à communiquer, ainsi que deux entiers, appelez-les Head et Tail. Head est un index dans le tableau, où l'élément suivant sera ajouté. Tail est un index dans le tableau, où l'élément suivant est disponible pour être supprimé. La tâche de producteur lit H et T pour déterminer s'il y a de la place pour ajouter un élément; écrit l'élément dans l'index H, puis met à jour H. Les tâches de consommation lisent H et T pour déterminer s'il y a des données disponibles, lit les données de l'index T, puis met à jour T. Fondamentalement, il s'agit d'un tampon en anneau auquel accèdent deux tâches, et le l'ordre des opérations (insérer, puis mettre à jour H; supprimer, puis mettre à jour T) garantit que la corruption des données ne se produit pas.

Si vous avez une situation avec plusieurs producteurs et un seul consommateur, ou un seul producteur et plusieurs consommateurs, vous avez effectivement une limitation de ressources quelconque, et il n'y a rien d'autre à faire que d'utiliser la synchronisation, car le limiteur de performances est plus susceptible de être le seul producteur / consommateur qu'un système d'exploitation avec le mécanisme de verrouillage.

Mais si vous avez plusieurs producteurs ET consommateurs, cela vaut la peine de passer du temps (dans l'espace de conception) pour voir si vous ne pouvez pas obtenir un mécanisme de communication plus coordonné; dans un cas comme celui-ci, la sérialisation de tout via une seule file d'attente fait définitivement de l'efficacité de la file d'attente le déterminant central des performances.


1
J'allais attribuer +1 à cela, mais vous vous trompez: des files d'attente sans verrouillage sont possibles à implémenter pour plusieurs lecteurs et écrivains, elles sont juste plus compliquées. (consultez l'article de Michael + Scott sur Lock-Free Queues google.com/search?q=michael%20scott%20queue )
Jason S

1
@Jason S - le document Scott revendique-t-il spécifiquement une ré-entrée pour les opérations d'insertion et de retrait sans verrouillage? Si c'est le cas, si vous pouvez extraire cela et le publier, faites-le, ce serait un atout inestimable pour beaucoup. Le lecteur doit noter que le document cité utilise des instructions machine spéciales, alors que ma position dans le poste ci-dessus ne supposait pas de telles instructions.
JustJeff

1
Oui, le coût des algorithmes sans verrouillage dépend généralement du CAS ou d'instructions équivalentes. Mais comment la rentrée entre-t-elle en jeu ici? Cela a du sens pour les mutex + structures de verrouillage, mais pas pour les opérations de structure de données.
Jason S

2

On peut obtenir un fonctionnement efficace dans une file d'attente mono-consommateur multi-producteur sans verrouillage si la file d'attente elle-même contient des éléments suffisamment petits pour fonctionner avec une primitive de magasin de charge, de comparaison-échange ou similaire, et on peut utiliser un valeur réservée ou valeurs réservées pour des emplacements de file d'attente vides. Lors de l'écriture dans la file d'attente, le rédacteur effectue un échange de comparaison pour essayer de stocker ses données dans le prochain emplacement vide; si cela échoue, le rédacteur essaie l'emplacement suivant. Bien que la file d'attente conserve un pointeur vers le prochain emplacement vide, la valeur du pointeur est "consultative". Notez que si un système utilise compare-exchange plutôt que load-store-exclusive, il peut être nécessaire d'avoir une «famille» de différentes valeurs de «slot vide». Sinon, si entre le moment où le rédacteur trouve un emplacement de file d'attente vide et tente d'y écrire, un autre écrivain écrit la fente et le lecteur la lit, le premier écrivain mettrait sans le savoir ses données dans un endroit où le lecteur ne les verrait pas. Ce problème ne se produit pas dans les systèmes qui utilisent load-store-exclusive, car le magasin-exclusif détecterait que les données ont été écrites même si elles ont été réécrites à l'ancienne valeur.


1

Vous pouvez accéder aux files d'attente plus efficacement en écrivant au-dessus de la file d'attente Normalement, la plupart des RTOS fournissent le support de l'ajout au début de la file d'attente, ce qui ne nécessite pas l'acquisition de mutex. Mais assurez-vous d'utiliser l'ajout au début de la file d'attente aussi peu que possible là où vous souhaitez simplement exécuter les données plus rapidement. Normalement, les structures de file d'attente ont une taille maximale, de sorte que vous ne pouvez pas mettre toutes les données dans la file d'attente, il est donc toujours facile de passer le pointeur.

à votre santé!!


1

Les files d'attente ne sont pas intrinsèquement lentes. Leur mise en œuvre peut être.

Si vous copiez aveuglément des données et utilisez une file d'attente synchrone, vous verrez un impact sur les performances.

Comme d'autres affiches l'ont indiqué, il existe des alternatives sans verrou. Le cas d'un seul producteur / seul consommateur est simple; pour plusieurs producteurs et consommateurs, l' algorithme de file d'attente sans verrou de Michael et Scott (ce sont leurs noms de famille) est la norme et est utilisé comme base pour ConcurrentLinkedQueue de Java .

Il est possible d'optimiser le besoin de files d'attente dans certains cas, mais ils offrent des garanties de concurrence qui offrent généralement d'énormes avantages de simplification aux systèmes en vous permettant de découpler les tâches.


D'après l'article de Michael & Scott: "c'est l'algorithme clair de choix pour les machines qui fournissent une primitive atomique universelle (par exemple comparer et échanger ou charger lié / stocker conditionnel)". Bien que cela ne verrouille pas exactement un thread, il existe une forme de synchronisation en cours ici.
JustJeff

vous avez un point; il peut diminuer l'exigence d'accès simultané d'un accès exclusif à une barrière de mémoire.
Jason S
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.