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.)?
la source
Réponses:
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).
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.
la source
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 à
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
la source
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ù avoirmalloc
un ATmega est bénéfique.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.
la source
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.
la source