Que se passe-t-il lorsque les microcontrôleurs manquent de RAM?


12

Ce n'est peut-être qu'une coïncidence, mais j'ai remarqué que les microcontrôleurs que j'ai utilisés ont redémarré lorsqu'ils manquaient de RAM (Atmega 328 si matériel spécifique). Est-ce ce que font les microcontrôleurs lorsqu'ils manquent de mémoire? Sinon, que se passe-t-il alors?

Pourquoi comment? Le pointeur de pile est certainement aveuglément augmenté à une plage de mémoire non allouée (ou survolé), mais que se passe-t-il alors: existe-t-il une sorte de protection qui le fait redémarrer ou est-ce (entre autres effets) le résultat de l'écrasement de critiques données (que je suppose différentes du code qui, je pense, est exécuté directement à partir du flash)?

Je ne suis pas sûr que cela devrait être ici ou sur Stack Overflow, s'il vous plaît laissez-moi savoir si cela doit être déplacé, même si je suis assez sûr que le matériel a un rôle à jouer.

Mise à jour

Je dois souligner que je suis particulièrement intéressé par le mécanisme réel derrière la corruption de mémoire (est-ce le résultat du roulement du SP -> cela dépend-il du mappage de la mémoire de l'UC, etc.)?


8
Certains micros seront réinitialisés si vous essayez d'accéder à des adresses invalides. C'est une fonctionnalité précieuse implémentée dans le matériel. D'autres fois, il peut finir par sauter vers un endroit arbitraire (disons que vous avez détruit l'adresse de retour pour un ISR), peut-être en exécutant des données plutôt que du code si l'architecture le permet, et peut-être en étant pris dans une boucle que le chien de garde fait ressortir. de.
Spehro Pefhany

2
Un processeur ne peut pas manquer de RAM, il n'y a aucune instruction qui le fera manquer de RAM. Le manque de RAM est entièrement un concept logiciel.
user253751

Réponses:


14

En général, la pile et le tas s'écroulent l'un sur l'autre. À ce stade, tout devient désordonné.

Selon le MCU, plusieurs choses peuvent (ou se produiront).

  1. Les variables sont corrompues
  2. La pile est corrompue
  3. Le programme est corrompu

Quand 1 arrive, vous commencez à avoir un comportement étrange - les choses ne font pas ce qu'elles devraient. Quand 2 se produisent, toutes sortes d'enfer se déchaînent. Si l'adresse de retour sur la pile (s'il y en a une) est corrompue, alors où l'appel en cours retournera est à la conjecture de n'importe qui. À ce moment-là, le MCU commencera à faire des choses au hasard. Quand 3 se produit à nouveau, qui sait ce qui se passerait. Cela ne se produit que lorsque vous exécutez du code hors de la RAM.

En général, lorsque la pile est corrompue, tout est fini. Tout ce qui se passe est dû au MCU.

Il se peut que la tentative d'allocation de la mémoire échoue en premier afin que la corruption ne se produise pas. Dans ce cas, le MCU peut déclencher une exception. S'il n'y a pas de gestionnaire d'exceptions installé, alors le plus souvent le MCU s'arrêtera simplement (un équivalent de while (1);. S'il y a un gestionnaire installé, alors il pourrait redémarrer proprement.

Si l'allocation de mémoire continue, ou si elle essaie, échoue et continue simplement sans mémoire allouée, alors vous êtes dans le royaume de "qui sait?". Le MCU pourrait finir par se redémarrer par la bonne combinaison d'événements (les interruptions ont causé la réinitialisation de la puce, etc.), mais il n'y a aucune garantie que cela se produise.

Ce qu'il peut généralement y avoir une forte probabilité de se produire, cependant, s'il est activé, c'est le temporisateur de surveillance interne (s'il en existe un) qui expire et redémarre la puce. Lorsque le programme passe complètement AWOL à travers ce type de plantage, les instructions pour réinitialiser le minuteur ne sont généralement pas exécutées, il expire et se réinitialise.


Merci pour votre réponse, c'est un excellent résumé des effets. Peut-être aurais-je dû préciser que j'aimerais plus de détails sur le mécanisme réel de ces corruptions: est-ce que la RAM entière est allouée à la pile et au tas, de sorte que le pointeur de pile survole et écrase les variables / adresses antérieures? Ou est-ce moins dépendant du mappage de la mémoire de chaque micro? Facultativement (c'est probablement un sujet en soi), je serais intéressé à apprendre comment ces gestionnaires de matériel sont mis en œuvre.
Monsieur Mystère

1
Cela dépend principalement du compilateur et de la bibliothèque C standard utilisée. Cela dépend aussi parfois de la configuration du compilateur (scripts de l'éditeur de liens, etc.).
Majenko

Pourriez-vous développer cela, peut-être avec quelques exemples?
Monsieur Mystère du

Non, pas vraiment. Certains systèmes allouent un espace fini pour différents segments, d'autres non. Certains utilisent des scripts de l'éditeur de liens pour définir des segments, d'autres non. Choisissez un microcontrôleur qui vous intéresse et faites des recherches sur le fonctionnement de ses allocations de mémoire.,
Majenko

12

Une vue alternative: les microcontrôleurs ne manquent pas de mémoire.

Du moins, pas lorsqu'il est correctement programmé. La programmation d'un microcontrôleur n'est pas exactement comme la programmation à usage général, pour le faire correctement, vous devez être conscient de ses contraintes et programmer en conséquence. Il existe des outils pour garantir cela. Recherchez-les et apprenez-les - au moins comment lire les scripts de l'éditeur de liens et les avertissements.

Cependant, comme Majenko et d'autres le disent, un microcontrôleur mal programmé peut manquer de mémoire, puis faire n'importe quoi, y compris une boucle infinie (ce qui donne au moins au chronomètre du chien de garde une chance de le réinitialiser. Vous avez activé le chronomètre du chien de garde, n'est-ce pas? )

Les règles de programmation courantes pour les microcontrôleurs évitent cela: par exemple, toute la mémoire est allouée sur la pile ou allouée statiquement (globalement); "nouveau" ou "malloc" sont interdits. Il en va de même pour la récursivité, de sorte que la profondeur maximale d'imbrication des sous-programmes peut être analysée et montrée pour tenir dans la pile disponible.

Ainsi, le stockage maximum requis peut être calculé lorsque le programme est compilé ou lié, et comparé à la taille de la mémoire (souvent encodée dans le script de l'éditeur de liens) pour le processeur spécifique que vous ciblez.

Le microcontrôleur peut alors ne pas manquer de mémoire, mais votre programme le pourrait. Et dans ce cas, vous arrivez à

  • réécrivez-le, plus petit ou
  • choisissez un processeur plus grand (ils sont souvent disponibles avec différentes tailles de mémoire).

Un ensemble commun de règles pour la programmation des microcontrôleurs est MISRA-C , adopté par l'industrie automobile.

À mon avis, la meilleure pratique consiste à utiliser le sous - ensemble SPARK-2014 d'Ada. Ada cible en fait raisonnablement bien les petits contrôleurs comme AVR, MSP430 et ARM Cortex, et fournit intrinsèquement un meilleur modèle de programmation de microcontrôleur que C.Mais SPARK ajoute des annotations au programme, sous forme de commentaires, qui décrivent ce que le programme fait.

Désormais, les outils SPARK analyseront le programme, y compris ces annotations, et en prouveront les propriétés (ou signaleront des erreurs potentielles). Vous n'avez pas à perdre de temps ou d'espace de code face aux accès à la mémoire erronés ou aux débordements d'entiers, car il est prouvé qu'ils ne se produisent jamais.

Bien qu'il y ait plus de travail initial impliqué avec SPARK, l'expérience montre qu'il peut arriver à un produit plus rapidement et moins cher parce que vous ne passez pas de temps à courir après des redémarrages mystérieux et d'autres comportements étranges.

Une comparaison de MISRA-C et SPARK


3
+1. Le portage malloc()(et son compagnon C ++ new) sur l'AVR est l'une des pires choses que les gens Arduino auraient pu faire, et a conduit de nombreux programmeurs très confus avec du code cassé à la fois sur leur forum et l'échange de pile Arduino. Il y a très, très peu de situations où avoir mallocun ATmega est bénéfique.
Connor Wolf

3
+1 pour la philosophie, -1 pour le réalisme. Si les choses étaient programmées correctement, il n'y aurait pas besoin de cette question. La question était de savoir ce qui se passe lorsque les microcontrôleurs manquent de mémoire. Comment les empêcher de manquer de mémoire est une autre question. Sur une autre note, la récursivité est un outil puissant, à la fois pour résoudre les problèmes et manquer de pile.
PkP

2
@Brian, comme je ne suis pas idiot, je suis évidemment d'accord avec toi. J'aime juste y penser en point de vue inversé - j'espère que lorsque vous réalisez les horribles conséquences de manquer de mémoire (pile), vous chercherez des moyens de l'empêcher de se produire. De cette façon, vous avez un réel élan pour trouver de bonnes pratiques de programmation au lieu de simplement suivre de bons conseils de programmation ... et lorsque vous atteignez la barrière de la mémoire, vous êtes plus susceptible d'appliquer les bonnes pratiques, même au détriment de la commodité. C'est juste un point de vue ...
PkP

2
@PkP: vous entendre haut et fort. J'ai étiqueté ceci comme une vue alternative - car cela ne répond pas vraiment à la question!
Brian Drummond

2
@ MisterMystère: Les microcontrôleurs ne manquent généralement pas de mémoire. Un microcontrôleur qui a 4096 octets de RAM lors de sa première mise sous tension aura 4096 octets pour toujours. Il est possible que le code essaie par erreur d'accéder à des adresses qui n'existent pas ou s'attendent à ce que deux méthodes différentes de calcul d'adresses accèdent à une mémoire différente dans le cas contraire, mais le contrôleur lui-même exécutera simplement les instructions qui lui sont données.
supercat

6

J'aime vraiment la réponse de Majenko et je l'ai attribuée moi-même. Mais je tiens à clarifier cela de façon précise:

Tout peut arriver quand un microcontrôleur manque de mémoire.

Vous ne pouvez vraiment pas compter sur quoi que ce soit quand cela arrive. Lorsque la machine manque de mémoire de pile, la pile est très probablement corrompue. Et comme cela arrive, tout peut arriver. Les valeurs variables, les déversements, les registres de température deviennent tous corrompus, perturbant les flux de programme. Si / alors / elses peut mal évaluer. Les adresses de retour sont tronquées, ce qui fait que le programme passe à des adresses aléatoires. Tout code que vous avez écrit dans le programme peut s'exécuter. (Considérez le code comme: "si [condition] alors {fire_all_missiles ();}"). De plus, un tas d'instructions que vous n'avez pas écrites peuvent s'exécuter lorsque le cœur passe à un emplacement de mémoire non connecté. Tous les paris sont levés.


2
Merci pour l'addendum, j'ai particulièrement aimé la ligne fire_all_missiles ().
Monsieur Mystère

1

L'AVR a réinitialisé le vecteur à l'adresse zéro. Lorsque vous écrasez la pile avec des déchets aléatoires, vous finirez par boucler et écraser une adresse de retour et cela pointera vers "nulle part"; puis lorsque vous revenez d'un sous-programme à cela nulle part, l'exécution fait une boucle vers l'adresse 0 où se trouve généralement un saut pour réinitialiser le gestionnaire.

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.