Domanda

ho incontrato un problema in un programma in C in esecuzione su un microcontroller AVR (ATMEGA328P). Credo che sia a causa di una collisione di stack / heap ma mi piacerebbe essere in grado di confermare questo.

C'è un modo posso visualizzare l'utilizzo SRAM dallo stack e heap?

Nota: il programma è compilato con avr-gcc e usa avr-libc

.

Aggiornamento: Il vero problema che sto avendo è che l'attuazione malloc non riesce (NULL ritorno). Tutti mallocing avviene all'avvio e tutto freeing avviene al termine dell'applicazione (che in pratica è mai poiché la parte principale dell'applicazione è in un ciclo infinito). Quindi sono sicuro che la frammentazione non è il problema.

È stato utile?

Soluzione

Dici malloc sta fallendo e il ritorno NULL:

La causa ovvio che si dovrebbe guardare in un primo momento è che il vostro heap è "pieno" - cioè, la memoria che hai chiesto di malloc non può essere allocato, perché non è disponibile.

Ci sono due scenari da tenere a mente:

A: Hai un mucchio di 16 K, hai già malloced 10 K e cercare di malloc un ulteriore 10K. Il tuo mucchio è semplicemente troppo piccolo.

b: Più comunemente, si dispone di un 16 k Mucchio, hai fatto un sacco di chiamate malloc / libera / realloc e il vostro mucchio è inferiore al 50% 'pieno': Si chiama malloc per 1K e non riesce - che cosa succede? Risposta - lo spazio di heap libero è frammentato - non c'è un contigui 1K di memoria libera che può essere restituito. manager C Heap non possono compattare il mucchio quando questo accade, quindi sei generalmente in un brutto modo. Ci sono tecniche per evitare la frammentazione, ma è difficile sapere se questo è davvero il problema. Avresti bisogno di aggiungere spessori di registrazione per malloc e libero in modo che si può avere un'idea di ciò che vengono eseguite le operazioni di memoria dinamica.

EDIT:

Dici tutti mallocs avvengono in fase di avvio, quindi la frammentazione non è il problema.

In questo caso, dovrebbe essere facile da sostituire l'allocazione dinamica con statico.

vecchio esempio di codice:

char *buffer;

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

nuovo codice:

char buffer[BUFFSIZE];

Una volta fatto questo in tutto il mondo, il tuo LINKER dovrebbe mettere in guardia voi, se non tutto può andare bene nella memoria disponibile. Non dimenticare di ridurre la dimensione heap -. Ma attenzione che alcuni runtime io le funzioni del sistema può ancora utilizzare il cumulo, quindi potrebbe non essere in grado di rimuovere del tutto

Altri suggerimenti

È possibile controllare l'utilizzo statico RAM utilizzando utility avr-size, come misura descritto in
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 ,
e http://letsmakerobots.com/node/27115

avr-size -C -x Filename.elf

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

Segue un esempio di come impostare questo su un IDE: Su Code :: Blocks, Progetto -> Crea opzioni -> pre / post costruire passaggi -> post-generazione passi, comprendono:

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

Esempio di output al termine di costruzione:

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) 

Dati è l'uso SRAM, ed è solo l'importo che il compilatore sa al momento della compilazione. È inoltre necessario spazio per le cose create a runtime (particolarmente impilare utilizzo).

Per controllare l'utilizzo dello stack (RAM dinamica), da http://jeelabs.org/2011/05/22/atmega-memory-use/

Ecco una piccola funzione di utilità che determina la quantità di RAM è attualmente inutilizzata:

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

Ed ecco uno schizzo utilizzando tale codice:

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

La funzione freeram () restituisce il numero di byte esiste tra la fine del cumulo e l'ultima memoria allocata in pila, quindi è efficace quanto la pila / mucchio può crescere prima che si scontrino.

Si potrebbe verificare il ritorno di questa funzione in tutto il codice si sospetta possa essere la causa dello stack / heap di collisione.

L'approccio usuale sarebbe per riempire la memoria con un modello noto e quindi controllare quali aree vengono sovrascritti.

Se si sta utilizzando sia stack e heap, allora può essere un po 'più complicato. Mi spiego quello che ho fatto quando non viene utilizzato alcun mucchio. Come regola generale, tutte le società per cui ho lavorato (nel dominio del software embedded C) hanno evitato usando heap per piccoli progetti-al incorporati evitare l'incertezza della disponibilità di memoria heap. Usiamo dichiarato staticamente variabili invece.

Un metodo è quello di riempire la maggior parte dell'area stack con un modello noto (ad esempio 0x55) all'avvio. Questo di solito è fatto da una piccola porzione di codice nelle prime fasi l'esecuzione del software, sia a destra all'inizio del main (), o forse anche prima di main () inizia, nel codice di start-up. Fare attenzione a non sovrascrivere la piccola quantità di pila in uso in quel punto del corso. Poi, dopo aver eseguito il software per un po ', ispezionare il contenuto di spazio di stack e vedere dove la 0x55 è ancora intatto. Come si "ispezionare" dipende dal vostro hardware di destinazione. Supponendo di avere un debugger collegato, allora si può semplicemente fermare il micro esecuzione e leggere la memoria.

Se si dispone di un debugger che può fare un punto di interruzione di memoria di accesso (un po 'più di fantasia del solito esecuzione breakpoint), quindi è possibile impostare un punto di interruzione in una posizione, ad esempio il limite estremo del vostro spazio di stack particolare stack. Che può essere estremamente utile, perché si mostra anche esattamente ciò po 'di codice viene eseguito quando raggiunge tale misura di utilizzo dello stack. Ma richiede il vostro debugger per supportare la funzione di memoria di accesso punto di rottura e non è spesso trovato nelle debugger "low-end".

Se si sta utilizzando anche mucchio, allora può essere un po 'più complicato perché potrebbe essere impossibile prevedere dove stack e heap si scontreranno.

Non utilizzare l'heap allocazione / dinamica sul target embedded. Soprattutto con un processore con tali risorse limitate. Piuttosto riprogettare l'applicazione perché il problema si ripresenterà come programma cresce.

Supponendo che si sta utilizzando una sola pila (quindi non un RTOS o qualsiasi cosa) e che la pila è alla fine della memoria, in crescita verso il basso, mentre il cumulo sta iniziando dopo la regione BSS / DATA, crescendo. Ho visto implementazioni di malloc che in realtà controllano l'stackpointer e non riescono in una collisione. Si potrebbe provare a farlo.

Se non siete in grado di adattare il codice malloc, si potrebbe scegliere di mettere il vostro stack all'inizio della memoria (utilizzando il file linker). In generale, è sempre una buona idea per conoscere / definire la dimensione massima dello stack. Se lo metti in partenza, si otterrà un errore di lettura oltre l'inizio della RAM. Il Mucchio sarà alla fine e può probabilmente non crescere oltre la fine, se si tratta di un implemantation decente (restituirà NULL invece). La cosa buona è che si sa avere 2 casi di errore separate per 2 questioni separate.

Per scoprire la dimensione massima dello stack, si potrebbe riempire la tua memoria con un modello, eseguire l'applicazione e vedere quanto lontano è andato, veda anche la risposta da Craig.

Se è possibile modificare il codice per l'heap, si potrebbe pad con un paio di byte extra (difficili su tali risorse bassi) su ogni blocco di memoria. Questi byte possono contenere un modello noto diversa dalla pila. Questo potrebbe dare un indizio, se si scontra con lo stack vedendo apparire all'interno della pila o viceversa.

In Unix come sistemi operativi una funzione di libreria denominata sbrk () con un parametro di 0 permette di accedere l'indirizzo più alto della memoria heap allocata dinamicamente. Il valore di ritorno è un puntatore void * e può essere confrontato con l'indirizzo di una variabile allocata pila arbitrario.

Usando il risultato di tale confronto deve essere usato con cautela. Seconda dell'architettura CPU e di sistema, la pila potrebbe essere crescente giù da un indirizzo arbitrario alto mentre il mucchio allocato si sposterà dalla memoria low-bound.

A volte il sistema operativo ha altri concetti per la gestione della memoria (cioè OS / 9) che pone mucchio e impilare in diversi segmenti di memoria nella memoria libera. Su questi sistemi operativi - in particolare per i sistemi embedded - è necessario definire i requisiti di memoria massima di tuo  applicazioni di anticipo per consentire al sistema di allocare segmenti di memoria di dimensioni corrispondenti.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top