Es kann nur ein Zufall sein, aber ich habe bemerkt, dass die von mir verwendeten Mikrocontroller neu gestartet wurden, als ihnen der RAM ausging (Atmega 328, falls hardwarespezifisch). Ist das das, was Mikrocontroller tun, wenn ihnen der Speicher ausgeht? Wenn nicht, was passiert dann?
Warum wie? Der Stapelzeiger wird sicherlich blind auf einen nicht zugewiesenen Speicherbereich erhöht (oder überrollt), aber was passiert dann: Gibt es eine Art Schutz, der ihn neu startet, oder ist er (neben anderen Effekten) das Ergebnis des Überschreibens von kritisch Daten (von denen ich annehme, dass sie sich von dem Code unterscheiden, der meiner Meinung nach direkt vom Flash ausgeführt wird)?
Ich bin mir nicht sicher, ob dies hier oder auf Stack Overflow sein sollte, bitte lassen Sie mich wissen, ob dies verschoben werden soll, obwohl ich mir ziemlich sicher bin, dass Hardware dabei eine Rolle spielt.
Ich sollte darauf hinweisen, dass ich mich besonders für den tatsächlichen Mechanismus hinter der Speicherbeschädigung interessiere (ist dies das Ergebnis des SP-Rollovers -> hängt das von der Speicherzuordnung des uC usw. ab)?
Im Allgemeinen prallen Stack und Heap aufeinander. An diesem Punkt wird alles chaotisch.
Abhängig von der MCU kann (oder wird) eines von mehreren Dingen passieren.
Wenn 1 passiert, bekommen Sie ein seltsames Verhalten - Dinge tun nicht das, was sie sollten. Wenn 2 passiert, bricht die Hölle los. Wenn die Rücksendeadresse auf dem Stack (falls vorhanden) beschädigt ist, kann man nur raten, wohin der aktuelle Aufruf zurückkehren wird. Zu diesem Zeitpunkt wird die MCU im Grunde anfangen, zufällige Dinge zu tun. Wenn 3 wieder passiert, wer weiß schon, was passieren würde. Dies geschieht nur, wenn Sie Code aus dem RAM ausführen.
Im Allgemeinen ist alles vorbei, wenn der Stack beschädigt wird. Was passiert, hängt von der MCU ab.
Es kann sein, dass der Versuch, den Speicher überhaupt zuzuweisen, fehlschlägt, sodass die Beschädigung nicht auftritt. In diesem Fall kann die MCU eine Ausnahme auslösen. Wenn kein Ausnahmehandler installiert ist, wird die MCU meistens einfach angehalten (ein Äquivalent von while (1);
. Wenn ein Handler installiert ist, wird sie möglicherweise sauber neu gestartet.
Wenn die Speicherzuordnung weitergeht oder wenn sie es versucht, fehlschlägt und einfach ohne zugewiesenen Speicher fortgesetzt wird, dann befinden Sie sich in den Bereichen "Wer weiß?". Die MCU startet sich möglicherweise durch die richtige Kombination von Ereignissen neu (durch Unterbrechungen, die zum Zurücksetzen des Chips usw. führen), aber es gibt keine Garantie dafür.
Was jedoch normalerweise mit hoher Wahrscheinlichkeit passieren kann, wenn es aktiviert ist, ist, dass der interne Watchdog-Timer (falls vorhanden) abläuft und den Chip neu startet. Wenn das Programm durch diese Art von Absturz völlig AWOL geht, werden die Anweisungen zum Zurücksetzen des Timers im Allgemeinen nicht ausgeführt, sodass er abläuft und zurückgesetzt wird.
Eine alternative Ansicht: Mikrocontrollern geht nicht der Speicher aus.
Zumindest nicht, wenn es richtig programmiert ist. Das Programmieren eines Mikrocontrollers ist nicht genau wie das Programmieren für allgemeine Zwecke. Um es richtig zu machen, müssen Sie sich seiner Einschränkungen bewusst sein und entsprechend programmieren. Es gibt Tools, die dabei helfen, dies sicherzustellen. Suchen Sie sie heraus und lernen Sie sie kennen - zumindest wie man Linker-Skripte und Warnungen liest.
Wie Majenko und andere jedoch sagen, kann ein schlecht programmierter Mikrocontroller keinen Speicher mehr haben und dann alles tun, einschließlich einer Endlosschleife (was dem Watchdog-Timer zumindest die Möglichkeit gibt, ihn zurückzusetzen. Sie haben den Watchdog-Timer aktiviert, nicht wahr? )
Übliche Programmierregeln für Mikrocontroller vermeiden dies: Zum Beispiel wird der gesamte Speicher entweder auf dem Stapel oder statisch (global) zugewiesen; "new" oder "malloc" sind verboten. Dies gilt auch für die Rekursion, sodass die maximale Tiefe der Verschachtelung von Unterprogrammen analysiert und gezeigt werden kann, dass sie in den verfügbaren Stapel passt.
Somit kann der maximal benötigte Speicherplatz berechnet werden, wenn das Programm kompiliert oder gelinkt wird, und mit der Speichergröße (häufig im Linker-Skript kodiert) für den spezifischen Prozessor verglichen werden, auf den Sie abzielen.
Dann geht dem Mikrocontroller möglicherweise nicht der Speicher aus, aber Ihrem Programm möglicherweise. Und in diesem Fall kommen Sie dazu
Ein gängiges Regelwerk für die Programmierung von Mikrocontrollern ist MISRA-C , das von der Automobilindustrie übernommen wurde.
Aus meiner Sicht ist es die beste Methode, die SPARK-2014- Teilmenge von Ada zu verwenden. Ada zielt tatsächlich ziemlich gut auf kleine Controller wie AVR, MSP430 und ARM Cortex ab und bietet von Natur aus ein besseres Modell für die Mikrocontroller-Programmierung als C. Aber SPARK fügt dem Programm Anmerkungen in Form von Kommentaren hinzu, die beschreiben, was das Programm tut.
Jetzt analysieren die SPARK-Tools das Programm, einschließlich dieser Anmerkungen, und beweisen Eigenschaften darüber (oder melden potenzielle Fehler). Sie müssen keine Zeit oder Codeplatz verschwenden, wenn Sie sich mit fehlerhaften Speicherzugriffen oder Integer-Überläufen befassen, da diese nachweislich nie vorkommen.
Obwohl mit SPARK mehr Vorlaufarbeit verbunden ist, zeigt die Erfahrung, dass es schneller und billiger zu einem Produkt kommen kann, da Sie keine Zeit damit verbringen, mysteriösen Neustarts und anderen seltsamen Verhaltensweisen nachzujagen.
malloc()
(und sein C++-Begleiter new
) auf den AVR ist eines der schlimmsten Dinge, die die Arduino-Leute hätten tun können, und hat zu vielen, vielen sehr verwirrten Programmierern mit fehlerhaftem Code sowohl in ihrem Forum als auch im Arduino-Stack-Austausch geführt. Es gibt sehr, sehr wenige Situationen, in denen malloc
ein ATmega von Vorteil ist.Ich mag Majenkos Antwort sehr und habe ihr selbst +1 gegeben. Aber ich möchte das auf den Punkt bringen:
Wenn einem Mikrocontroller der Speicher ausgeht, kann alles passieren.
Sie können sich wirklich auf nichts verlassen, wenn es passiert. Wenn dem Computer der Stapelspeicher ausgeht, wird der Stapel höchstwahrscheinlich beschädigt. Und wenn das passiert, kann alles passieren. Variablenwerte, Überläufe, temporäre Register werden alle beschädigt und unterbrechen den Programmablauf. If/then/elses kann falsch auswerten. Absenderadressen werden verstümmelt, wodurch das Programm zu zufälligen Adressen springt. Jeder Code, den Sie in das Programm geschrieben haben, kann ausgeführt werden. (Betrachten Sie Code wie: "if [condition] then {fire_all_missiles();}"). Auch eine ganze Reihe von Anweisungen, die Sie nicht geschrieben haben, können ausgeführt werden, wenn der Kern zu einem nicht verbundenen Speicherplatz springt. Alle Wetten sind aus.
Der AVR hat den Vektor an der Adresse Null zurückgesetzt. Wenn Sie den Stapel mit zufälligem Müll überschreiben, werden Sie schließlich eine Schleife machen und eine Rücksprungadresse überschreiben, die auf "nirgendwo" zeigt. Wenn Sie dann von einer Subroutine zu diesem Nirgendwo zurückkehren, wird die Ausführung eine Schleife zur Adresse 0 machen, wo sich normalerweise ein Sprung zum Reset-Handler befindet.
Spehro Pefhany
Benutzer253751