Comment puis-je effectuer des transferts de données dynamiques et la gestion de la mémoire entre les threads en C?

StackOverflow https://stackoverflow.com/questions/689514

  •  22-08-2019
  •  | 
  •  

Question

Plate-forme: ARM9

langage de programmation C

- Exigences. Plaine C et pas libs externes et aucun coup de pouce

OS - REX RTOS

I ai deux fils en cours d'exécution dans une plate-forme embarquée -

  1. un est au niveau du pilote de manutention tous les comms et le transfert de données avec le matériel.
  2. le second thread exécute l'application qui utilise les données / du matériel.

L'idée est de découpler le fil d'application du fil conducteur afin que nous puissions changer le matériel et la mise en œuvre dans le fil conducteur du matériel, mais ont un impact minimal sur le fil d'application.

Mon défi est que les données reçues du matériel peut être dynamique dire que nous ne savons pas dès le départ la quantité de mémoire le fil d'application doit mettre de côté pour chaque demande de / du matériel comme cela est déterminé lors de l'exécution.

Je pensais le fil conducteur pourrait informer le fil d'application qu'il ya tellement de données à lire. Le fil d'application alloue alors la mémoire et demande au fil de conducteur de lire les données. Il est alors au fil d'application pour traiter les données en conséquence. De cette façon, toute la gestion de la mémoire est dans le fil d'application.

Était-ce utile?

La solution

Options couples viennent à l'esprit:

1) malloc la mémoire dans le pilote, il libère dans l'application. Mais ... nous avons tendance à ne pas utiliser malloc dans tout ce qui se rapproche d'une exigence en temps réel. Si vous avez accès à malloc / gratuit, et il n'y a pas d'inquiétude « en temps réel » ou des problèmes de fragmentation de la mémoire (à savoir votre tas est assez grand), alors c'est une approche assez facile. Le pilote envoie simplement le pointeur alloué au fil d'application via une file d'attente de messages et l'application est gratuit la mémoire lorsque vous avez terminé. Méfiez-vous des fuites de mémoire.

2) le cycle ou des tampons circulaires. Le pilote gère complètement un tampon circulaire de taille fixe et envoie simplement un message à l'application quand un tampon est prêt. Voir ici pour quelques détails: mémoire tampon circulaire . Ensuite, l'application marque à nouveau les données « disponibles » via une API de pilote, ce qui permet de masquer les détails du tampon anneau du fil de l'application. Nous utilisons cette approche pour un de nos pilotes qui a un ensemble très similaire des exigences que vous décrivez. Dans ce cas, vous devez se préoccuper de déterminer la taille « meilleur » pour le tampon circulaire, la manipulation trop-plein dans le pilote, etc.

bonne chance!

Autres conseils

Vous ne spécifiez pas un système d'exploitation, mais il faut en quelque sorte « fils ». Sauf que l'un d'entre eux est au niveau du pilote (gestionnaire d'interruption) et les autres sons comme une application (userland / kernel). Mais cela ne correspond pas non plus, parce que votre pilote et l'application communiquent avant que les données sont même traitées.

Votre terminologie est source de confusion et non encourageante. Est-ce un homebrew (RT) OS ou non?

Si vous avez un vrai système d'exploitation, il existe des méthodes pour les conducteurs mis en place et l'écriture des données remettant à userland. Lisez la documentation ou utiliser l'un des pilotes existants comme référence.

Si cela est un système d'exploitation personnalisé, vous pouvez toujours se référer à d'autres pilotes open source pour des idées, mais vous clairement pas les choses mises en place aussi facilement. Préallouer toute la mémoire dans le code du pilote, le remplir avec des données qu'il arrive, et la transférer au code de l'application. La quantité de mémoire sera fonction de la rapidité de votre application peut traiter des données, la plus grande quantité de données que vous prévoyez d'accepter, et comment faire la queue de données beaucoup plus interne est nécessaire pour soutenir votre application.

Cet être C, j'ai fini par avoir à faire l'application enregistrer un rappel avec le pilote. Le but de la fonction de rappel est de traiter les données une fois le pilote, il lit à partir de l'appareil. Le pilote gère la mémoire à savoir Alloue de la mémoire, invoque le rappel et libère enfin la mémoire. En outre, le rappel a seulement la permission de lecture sur la mémoire. Par conséquent, l'application devrait idéalement juste copier le contenu du tampon à sa propre mémoire et la sortie de la fonction de rappel tout de suite. Ensuite, il est libre de traiter les données quand et comment il le souhaite.

Je mis à jour la documentation à préciser aux usages du rappel de l'application que l'on suppose quand le retour de rappel, la mémoire ne doivent plus être considérés comme valides. Si le rappel est utilisé d'une autre manière, le comportement est indéfini.

Ma première pensée serait d'utiliser des tampons circulaires. Voici quelques exemples de code. Sentez-vous libre d'adapter cela à vos propres utilisations. Vous voudriez probablement pas les variables globales. Et vous ne voulez pas le #defines:

#define LENGTH (1024)
#define MASK (LENGTH-1)
uint8 circularBuffer[ LENGTH ];
int circularBuffer_add = 0;
int circularBuffer_rmv = 0;

void copyIn( uint8 * circularBuffer, uint8 * inputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        circularBuffer[ circularBuffer_add ] = inputBuffer[ i ];
        circularBuffer_add = ( circularBuffer_add + 1 ) & MASK;
    } 
}

void copyOut( uint8 * circularBuffer, uint8 * outputBuffer, int n ) {
    int i;
    for( i = 0; i < n; i++ ) {
        outputBuffer[ i ] = circularBuffer[ circularBuffer_rmv ];
        circularBuffer_rmv = ( circularBuffer_rmv + 1 ) & MASK;
    } 
}

De plus, le code ci-dessus suppose que votre unité de données est le type de données « uint8 ». Vous pouvez le modifier pour qu'il utilise un autre type de données. Ou vous pouvez même en faire memcpy générique et l'utilisation () pour copier dans le buffer circulaire.

La principale caractéristique de ce code est la façon dont il gère l'ajout et RMV ptr.


Une fois que vous obtenez des choses de travail avec le code ci-dessus. Je suggère à un changement de points sur toutes vos lectures du matériel à utiliser -mémoire- directe accès API .

Il est important pour passer à la mémoire à accès direct car le code ci-dessus utilise un grand nombre de cycles par rapport à DMA qui utilise presque zéro cycles.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top