Frage

Ich habe ein Problem in einem C-Programm läuft auf einem AVR-Mikrocontroller (ATMega328P) angetroffen. Ich glaube, es zu einem Stapel / Haufen Kollision zurückzuführen ist, aber ich möchte, dies bestätigen können.

Gibt es eine Möglichkeit, SRAM-Nutzung durch den Stack und der Heap visualisieren kann?

Hinweis: wird das Programm mit avr-gcc kompiliert und verwendet avr-libc

.

Update: Das eigentliche Problem, das ich habe ist, dass die malloc-Implementierung (Rückkehr NULL) versagt. All mallocing geschieht beim Start und alle freeing geschehen am Ende der Anwendung (was in der Praxis nie da der Hauptteil der Anwendung in einer Endlos-Schleife ist). Also ich bin sicher, dass die Fragmentierung ist nicht das Problem.

War es hilfreich?

Lösung

Sie sagen malloc fehlschlägt und die Rückkehr NULL:

Die offensichtliche Ursache, die man auf den ersten Blick sollte, ist, dass Ihr Haufen „voll“ ist - das heißt, der Speicher Sie malloc gefragt haben, können nicht zugeordnet werden, da es nicht verfügbar ist.

Es gibt zwei Szenarien zu beachten:

a: Sie haben einen 16 K Haufen, haben Sie malloced bereits 10 K und Sie versuchen, eine weitere 10K malloc. Ihr Heap ist einfach zu klein.

b: Häufiger Sie haben einen 16 k Heap, Sie haben eine Reihe von malloc / free / realloc Anrufe getan und Ihre Heap ist weniger als 50% ‚voll‘: Sie rufen malloc für 1K und es ausfällt - wie geht's? Antwort - der Haufen freier Speicherplatz vorhanden ist zersplittert - es gibt keine contigous 1K den freien Speicher, die zurückgegeben werden können. C Heap-Manager können nicht den Heap komprimieren, wenn dies geschieht, so sind Sie in der Regel in einem schlechten Weg. Es gibt Techniken, die Fragmentierung zu vermeiden, aber es ist schwierig zu wissen, ob dies wirklich das Problem ist. Sie müssen die Protokollierung Unterlegscheiben zu malloc und frei hinzuzufügen, damit Sie eine Vorstellung davon, was dynamische Speicher erhalten können Operationen ausgeführt werden.

EDIT:

Sie sagen alle mallocs beim Start passieren, so Fragmentierung ist nicht das Problem.

In diesem Fall sollte es einfach sein, die dynamische Zuordnung mit statischem zu ersetzen.

altes Code-Beispiel:

char *buffer;

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

Neuer Code:

char buffer[BUFFSIZE];

Wenn Sie dies überall getan haben, sollten Sie Ihre LINKER Sie warnen, wenn alles, was nicht in den Speicher verfügbar passen. Vergessen Sie nicht, die Heap-Größe zu reduzieren -. Aber Vorsicht, dass einige Runtime-io Systemfunktionen noch den Heap verwenden, so dass Sie nicht in der Lage sein, kann es vollständig zu entfernen

Andere Tipps

Sie können die RAM überprüfen statische Verwendung avr-size Dienstprogramm, wie in
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 ,
und http://letsmakerobots.com/node/27115

avr-size -C -x Filename.elf

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

Folgt ein Beispiel, wie diese Einstellung vornehmen, auf einem IDE: Auf Code :: Blocks, Projekt -> Optionen Erstellen -> Pre / Post Schritte bauen -> Schritte Post-bauen, sind:

avr-size -C $(TARGET_OUTPUT_FILE) oder in avr-size -C --mcu=atmega328p $(TARGET_OUTPUT_FILE)

Beispiel Ausgang am Ende des Build:

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) 

Die Daten sind Ihre SRAM-Nutzung, und es ist nur die Menge, dass der Compiler weiß bei der Kompilierung. Sie müssen auch Raum für erschaffenen Dinge an Laufzeit (insbesondere Stack-Nutzung).

Stackverbrauch (dynamischer RAM) zu überprüfen, von http://jeelabs.org/2011/05/22/atmega-memory-use/

Hier ist ein kleines Programm, Funktion, wie viel RAM bestimmt ist derzeit nicht verwendet:

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

Und hier ist eine Skizze, den Code verwendet:

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

Die freeram () Funktion gibt, wie viele Bytes zwischen dem Ende des Haufens und dem letzten zugewiesenen Speicher auf dem Stack vorhanden ist, so ist es effektiv, wie viel der Stapel / Haufen wachsen können, bevor sie kollidieren.

Sie könnten die Rückkehr dieser Funktion um Code überprüfen Sie vermuten, dass Stack / Heap-Kollision kann verursachen.

Der übliche Ansatz wäre, den Speicher mit einem bekannten Muster zu füllen und dann, welche Bereiche zu überprüfen, werden überschrieben.

Wenn Sie beide Stack und Heap verwendet wird, dann kann es ein wenig schwierig sein. Ich werde erklären, was ich getan habe, wenn kein Haufen verwendet wird. In der Regel sind alle Unternehmen, die ich für (im Bereich der Embedded-C-Software) gearbeitet habe, haben mit Heap für kleine eingebettete Projekte zu vermeiden, die Unsicherheit des Heap-Speichers Verfügbarkeit zu vermeiden. Wir verwenden statisch Variablen anstelle erklärt.

Ein Verfahren ist mit einem bekannten Muster (z.B. 0x55) bei der Inbetriebnahme der größten Teil des Stapelbereiches zu füllen. Dies wird in der Regel durch ein kleines Stück Code früh in der Software-Ausführung erfolgt entweder direkt am Anfang des main (), oder vielleicht sogar vor main () beginnt, in dem Start-up-Code. Achten Sie darauf, nicht die kleine Menge des Stapels im Einsatz zu überschreiben zu diesem Zeitpunkt natürlich. Dann, nachdem die Software für eine Weile ausgeführt wird, überprüfen Sie die Inhalte von Stack-Raum und sehen, wo die 0x55 noch intakt ist. Wie Sie „inspizieren“ hängt von Ihrer Zielhardware. Angenommen, Sie haben einen Debugger angeschlossen ist, dann können Sie einfach den Mikrolauf stoppen und den Speicher eingelesen.

Wenn Sie einen Debugger haben, die einen Speicherzugriffsunterbrechungs tun können (ein bisschen mehr Phantasie als die übliche Ausführung Breakpoint), dann können Sie einen Haltepunkt in einem bestimmten Stapel orts wie die äußerste Grenze des Stapelspeicher gesetzt. Das kann sehr nützlich sein, weil es Ihnen zeigt auch genau das, was wenig Code ausgeführt wird, wenn es, dass Ausmaß der Stackverbrauch erreicht. Aber es erfordert Ihren Debugger die Speicherzugriffsbreakpoint-Funktion und es ist oft nicht gefunden in dem „Low-End“ Debuggers zu unterstützen.

Wenn Sie auch Heap verwenden, dann kann es etwas komplizierter sein, weil es unmöglich sein kann, vorherzusagen, wo Stack und Heap kollidieren.

Verwenden Sie nicht den Heap / dynamische Zuordnung auf embedded Targets. Gerade bei einem Prozessor mit einem solchen begrenzten Ressourcen. Vielmehr Ihre Anwendung neu zu gestalten, weil das Problem wird erneut auftreten, wie Ihr Programm wächst.

Sie verwenden nur einen Stapel Unter der Annahme, (also nicht ein RTOS oder etwas), und dass der Stapel am Ende des Speichers ist, nach unten wächst, während der Haufen nach der BSS / DATA Region beginnen wird, aufwächst. Ich habe Implementierungen von malloc gesehen, die tatsächlich die Stackpointer überprüfen und nicht auf einer Kollision. Sie könnten versuchen, das zu tun.

Wenn Sie nicht in der Lage sind, den malloc Code anzupassen, könnten Sie wählen Ihren Stack zu Beginn des Speichers zu setzen (die Linker-Datei). Im Allgemeinen ist es immer eine gute Idee zu wissen / definiert die maximale Größe des Stapels. Wenn Sie es zu Beginn setzen, wird ein Fehler beim Lesen über den Anfang des RAM erhalten. Der Heap am Ende sein wird, und kann wahrscheinlich nicht über das Ende wachsen, wenn es ein ordentliches UMSETZEN (wird NULL zurückgeben, statt). Gute Sache ist, Sie wissen, haben 2 separate Fehlerfälle für 2 seperate Ausgaben.

Um die maximale Stapelgröße herauszufinden, können Sie Ihren Speicher mit einem Muster füllen könnten, die Anwendung ausführen und sehen, wie weit es ging, siehe auch von Craig antworten kann.

Wenn Sie den Code für Ihren Haufen bearbeiten können, könnten Sie Pad es mit ein paar zusätzlichen Bytes (tricky auf so niedrige Ressourcen) auf jedem Speicherblock. Diese Bytes enthalten könnte ein bekanntes Muster unterscheidet sich von dem Stapel. Dies könnte Ihnen einen Hinweis geben, wenn es mit dem Stapel kollidiert, indem sie es innerhalb des Stapels oder umgekehrt erscheinen zu sehen.

Auf Unix-ähnlichen Betriebssystemen eine Bibliotheksfunktion namens sbrk () mit einem Parameter von 0 können Sie die oberste Adresse des dynamisch zugewiesenen Heap-Speicher zuzugreifen. Der Rückgabewert ist ein void * Zeiger und könnte mit der Adresse eines beliebigen Stapels zugewiesene Variable verglichen werden.

Mit dem Ergebnis dieses Vergleichs sollte mit Vorsicht verwendet werden. In Abhängigkeit von der CPU und Systemarchitektur kann der Stapel aus einer beliebigen hohen Adresse wächst, während die zugeordneten Haufen von niedrigem gebundenen Speicher bewegen werden.

Manchmal hat das Betriebssystem andere Konzepte für die Speicherverwaltung (d OS / 9), die Halde platziert und in verschiedenen Speichersegmenten im freien Speicher stapelt. Auf diesen Betriebssystemen - vor allem für eingebettete Systeme - Sie brauchen, um den maximalen Speicherbedarf Ihrer definieren  Anwendungen im voraus, damit das System Speichersegmente von passenden Größen zuzuordnen.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top