Question

Je l'ai rencontré un problème dans un programme C en cours d'exécution sur un microcontrôleur AVR (ATMEGA328P). Je crois que cela est dû à une collision pile / tas mais je voudrais être en mesure de le confirmer.

Est-il possible que je peux visualiser l'utilisation de la SRAM par la pile et le tas?

Remarque: le programme est compilé avec gcc-et AVR utilise avr-libc

.

Mise à jour: Le problème réel que j'ai est que la mise en œuvre de malloc échoue (retour NULL). Tout se passe mallocing au démarrage et tout freeing se produit à la fin de l'application (qui en pratique est jamais depuis la partie principale de l'application est dans une boucle infinie). Je suis donc sûr que la fragmentation n'est pas la question.

Était-ce utile?

La solution

Vous dites que malloc échoue et retour NULL:

La cause évidente que vous devriez regarder d'abord que votre tas est « plein » - i.e., la mémoire que vous avez demandé à malloc ne peut être attribué, car ce n'est pas disponible.

Il y a deux scénarios à garder à l'esprit:

a: Vous avez un 16 K tas, vous avez déjà malloced 10 K et vous essayez de malloc un autre 10K. Votre tas est tout simplement trop petit.

b: Plus généralement, vous avez 16 k Heap, vous avez fait un tas de malloc / gratuit / appels realloc et votre tas est inférieur à 50% « full »: Vous appelez malloc pour 1K et il ne réussit pas - quoi de neuf? Réponse - l'espace libre de tas est fragmenté - il n'y a pas 1K Contigous de mémoire libre qui peut être retourné. gestionnaires C Heap ne peuvent compacter le tas quand cela arrive, vous êtes généralement une mauvaise façon. Il existe des techniques pour éviter la fragmentation, mais il est difficile de savoir si cela est vraiment le problème. Vous aurez besoin d'ajouter des cales d'exploitation forestière à malloc et libre afin que vous puissiez avoir une idée de ce que les opérations de mémoire dynamique sont effectuées.

EDIT:

Vous dites que tous les mallocs se produisent au démarrage, si la fragmentation n'est pas la question.

Dans ce cas, il devrait être facile de remplacer l'allocation dynamique statique.

ancien exemple de code:

char *buffer;

void init()
{
  buffer = malloc(BUFFSIZE);
}

nouveau code:

char buffer[BUFFSIZE];

Une fois que vous avez fait cela partout, votre LINKER devrait vous avertir si tout ne peut pas entrer dans la mémoire disponible. Ne pas oublier de réduire la taille du tas -. Mais méfiez-vous que certaines fonctions d'exécution io système peut encore utiliser le tas, de sorte que vous ne pouvez pas être en mesure de retirer complètement

Autres conseils

Vous pouvez vérifier l'utilisation statique RAM en utilisant l'utilitaire de avr-size, comme decribed
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=62968 ,
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=82536 ,
http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=95638 ,
et http://letsmakerobots.com/node/27115

avr-size -C -x Filename.elf

(documentation avr-size: http://ccrma.stanford.edu/planetccrma /man/man1/avr-size.1.html)

Suit un exemple de la façon de mettre sur un IDE: Le Code :: Blocks, Projet -> Options de construction -> Pré / post construire étapes -> Post-construire étapes, comprennent:

avr-size -C $(TARGET_OUTPUT_FILE) ou
avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)

Exemple de sortie à la fin de la construction:

AVR Memory Usage
----------------
Device: atmega16

Program:    7376 bytes (45.0% Full)
(.text + .data + .bootloader)

Data:         81 bytes (7.9% Full)
(.data + .bss + .noinit)

EEPROM:       63 bytes (12.3% Full)
(.eeprom) 

données est votre utilisation de SRAM, et il est seulement le montant que le compilateur sait au moment de la compilation. Vous avez également besoin de place pour les choses créées à exécution (empiler en particulier l'utilisation).

Pour vérifier l'utilisation de la pile (mémoire vive dynamique), de http://jeelabs.org/2011/05/22/atmega-memory-use/

Voici une petite fonction d'utilité qui détermine la quantité de RAM est actuellement utilisé:

int freeRam () {
  extern int __heap_start, *__brkval; 
  int v; 
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval); 
}

Et voici une esquisse à l'aide de ce code:

void setup () {
    Serial.begin(57600);
    Serial.println("\n[memCheck]");
    Serial.println(freeRam());
}

La fonction freeram () retourne le nombre d'octets existe entre la fin du tas et la dernière mémoire allouée sur la pile, il est effectivement combien la pile / tas peut se développer avant d'entrer en collision.

Vous pouvez vérifier le retour de cette fonction autour de code que vous pensez peut causer une collision pile / tas.

L'approche habituelle serait de remplir la mémoire avec un modèle connu, puis de vérifier quelles zones sont écrasées.

Si vous utilisez à la fois pile et tas, alors il peut être un peu plus délicat. Je vais vous expliquer ce que je l'ai fait quand aucun tas est utilisé. En règle générale, toutes les entreprises que j'ai travaillé (dans le domaine des logiciels embarqués C) ont évité d'utiliser tas pour les petits projets à embarqués éviter l'incertitude de la disponibilité de la mémoire de tas. Nous utilisons statiquement variables déclarées à la place.

Une méthode consiste à remplir la majeure partie de la zone de pile avec une configuration connue (par exemple 0x55) au démarrage. Cela se fait généralement par un petit bout de code au début de l'exécution du logiciel, soit dès le début de la main (), ou peut-être même avant main () commence, dans le code de démarrage. Prenez soin de ne pas écraser la petite quantité de pile utilisée à ce moment-là bien sûr. Puis, après l'exécution du logiciel pendant un certain temps, inspecter le contenu de l'espace de pile et de voir où le 0x55 est encore intact. Comment vous « inspectez » dépend de votre matériel cible. En supposant que vous avez un débogueur connecté, vous pouvez simplement arrêter le micro course et lire la mémoire.

Si vous avez un débogueur qui peut faire un point d'arrêt d'accès mémoire (un peu plus de fantaisie que le point d'arrêt d'exécution habituelle), vous pouvez définir un point d'arrêt dans un emplacement tel empilement particulier que la limite extrême de votre espace de pile. Cela peut être extrêmement utile, car il vous montre aussi exactement ce morceau de code est en cours d'exécution quand il atteint cette ampleur de l'utilisation de la pile. Mais il exige que votre débogueur pour soutenir la fonction de mémoire breakpoint d'accès et il est souvent trouvé dans les débogueurs « bas de gamme ».

Si vous utilisez également tas, alors il peut être un peu plus compliqué car il peut être impossible de prédire où vont entrer en collision pile et tas.

Ne pas utiliser le tas / allocation dynamique sur les cibles embarquées. Surtout avec un processeur avec des ressources limitées. Plutôt redessiner votre application parce que le problème se reproduira que votre programme se développe.

En supposant que vous utilisez une seule pile (donc pas un RTOS ou quoi que ce soit) et que la pile est à la fin de la mémoire, de plus en plus vers le bas, tandis que le tas commence après la région BSS / DATA, la croissance. J'ai vu des implémentations de malloc qui vérifient en fait la StackPointer et échouent sur une collision. Vous pouvez essayer de le faire.

Si vous n'êtes pas en mesure d'adapter le code malloc, vous pouvez choisir de mettre votre pile au début de la mémoire (en utilisant le fichier de liaison). En général, il est toujours une bonne idée de savoir / définir la taille maximale de la pile. Si vous le mettez au début, vous obtiendrez une erreur à la lecture au-delà du début de la RAM. Le Tas sera à la fin et ne peut probablement pas se développer au-delà de la fin si elle est un implemantation décent (retournera NULL à la place). Bonne chose est que vous savez que 2 cas d'erreur séparés pour 2 numéros distincts.

Pour connaître la taille maximale de la pile, vous pouvez remplir votre mémoire avec un motif, exécutez l'application et voir jusqu'où il est allé, voir également la réponse de Craig.

Si vous pouvez modifier le code pour votre tas, vous pad pourrait avec quelques octets supplémentaires (délicate sur ces faibles ressources) sur chaque bloc de mémoire. Ces octets peuvent contenir un motif connu différent de la pile. Cela pourrait vous donner un indice si elle entre en collision avec la pile en le voyant apparaître à l'intérieur de la pile ou vice versa.

Sur Unix comme système d'exploitation une fonction bibliothèque nommée sbrk () avec un paramètre de 0 vous permet d'accéder à l'adresse la plus haute de la mémoire de tas allouée dynamiquement. La valeur de retour est un pointeur void * et peut être comparée avec l'adresse d'une pile arbitraire variable allouée.

En utilisant le résultat de cette comparaison doit être utilisée avec précaution. En fonction de l'architecture du processeur et du système, la pile peut être de plus en plus vers le bas à partir d'une adresse haute arbitraire alors que le tas alloué se déplacer vers le haut de la mémoire à faible liée.

Parfois, le système d'exploitation a d'autres concepts pour la gestion de la mémoire (c.-à-OS / 9) qui place tas et de la pile dans les différents segments de mémoire dans la mémoire libre. Sur ces systèmes d'exploitation - en particulier pour les systèmes embarqués - vous devez définir les besoins en mémoire maximale de votre  applications à l'avance pour permettre au système d'allouer des segments de mémoire de tailles correspondantes.

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