Uso di malloc in PIC


10

Come posso usare malloc()e free()funzioni in un PIC? Ho controllato l' stdlib.hintestazione e non si fa menzione di loro. Sto usando MCC18.

Qualcuno ha dovuto usarli?

Ho bisogno di loro perché sto effettuando il porting di una libreria da Windows XP al PIC. La guida al porting dice a

adattare le funzioni specifiche del sistema operativo a quelle dei miei PIC

Ma non so come "tradurre" le funzioni malloc()e free().


4
Prova a utilizzare l'allocazione statica, se possibile.
Nick T,

1
Perché? Il problema è che sto scrivendo il livello inferiore (quello specifico della piattaforma) di una libreria piuttosto grande e molte funzioni per cui non ho idea di cosa stiano usando questo .. e non ho idea di come cambiare dinamico a statico ..
stef

11
Sembra che un microcontrollore PIC con <4KB RAM potrebbe essere sbagliato per la tua applicazione. Su un PC, misurare l'utilizzo della memoria della libreria del PC prima di iniziare una porta. Potresti stare meglio con qualcosa di più robusto come un ARM Cortex-M3. Regola empirica: se la base di codice che stai eseguendo il porting è troppo grande per essere compresa, non si adatta a un PIC.
Toby Jaffey,

I driver di Windows (e le applicazioni in generale) sono essenzialmente scritti con un paradigma di "RAM illimitata", poiché se la RAM fisica è esaurita, la memoria virtuale può essere scambiata. A seconda di ciò che la libreria sta facendo, potrebbe benissimo consumare più del 4kB disponibile nel PIC18F87J11. Ho il sospetto che non sarai in grado di misurare quanta memoria utilizzerà il driver.
Adam Lawrence,

Un altro potenziale problema: un int Win32 è 32 bit, mentre con il compilatore MCC18, è solo 16 bit. Potresti avere strani problemi di overflow se non stai attento.
Adam Lawrence,

Risposte:


8

In molte applicazioni, sarà necessario allocare memoria, ma non sarà necessario liberare nulla mantenendo qualcosa che è stato allocato dopo di esso. Su un tale sistema, tutto ciò che occorre fare è utilizzare il linker per definire un array usando tutta la RAM disponibile, impostare un puntatore all'inizio di tale array e quindi utilizzare una funzione malloc facile e intuitiva:

char * next_alloc;
void * malloc (int size)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <dimensione)
      ritorno -1;
    next_alloc + = size;
    restituisce this_alloc;
}
void free (void * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}

Simpatico e semplice, con solo due byte di sovraccarico totale per qualsiasi numero di allocazioni. La chiamata gratuita () su un blocco dislocerà quel blocco e tutto ciò che lo segue.

I modelli di allocazione leggermente più complicati possono essere gestiti utilizzando due puntatori: uno che alloca gli oggetti dal fondo della memoria verso l'alto e uno che va dall'alto verso il basso verso il basso. È anche possibile utilizzare un garbage collector compattante se i dati nell'heap sono omogenei e si sa dove si trovano tutti i riferimenti esterni ad esso.


Per essere completamente informato sulle possibilità, leggi la risposta su electronics.stackexchange.com/questions/7850/…
gavioto20

14

malloc()nei microcontrollori è generalmente considerata una "cosa negativa". Ma, se ne hai assolutamente bisogno, ti consigliamo di trovare una versione di terze parti.

Se sei fortunato, il codice che stai trasferendo potrebbe non fare affidamento sul riutilizzo di blocchi di memoria. In questo caso, è possibile scrivere un semplice allocatore che restituisce un puntatore in un buffer RAM, quindi fa avanzare il puntatore in base alla dimensione del blocco richiesta.

Ho usato con successo questo approccio prima di eseguire il porting delle librerie PC su microcontrollori.

Di seguito, dovresti impostare l'allocatore con my_malloc_init()e allocare memoria con my_malloc(). my_free()è lì per soddisfare la dipendenza ma in realtà non farà nulla. Alla fine rimarrai senza spazio, ovviamente.

Per far funzionare tutto ciò, dovrai misurare il requisito di memoria peggiore del tuo codice (fallo su un PC se possibile), quindi imposta di HEAP_SIZEconseguenza. Prima di accedere alla parte della libreria che richiede memoria dinamica, chiamare my_malloc_init(). Prima di riutilizzarlo, assicurarsi che nulla punti ancora heap.

uint8_t heap[HEAP_SIZE];
uint8_t *heap_ptr;

void my_malloc_init(void)
{
    heap_ptr = heap;
}

void *my_malloc(size_t len)
{
    uint8_t *p = heap_ptr;
    heap_ptr += len;
    if (heap_ptr >= heap + HEAP_SIZE)
        return NULL;
    else
        return p;
}

void my_free(void)
{
    // do nothing
}

(nota: nel mondo reale, potrebbe essere necessario considerare l'allineamento del puntatore, ovvero arrotondare per eccesso heap_ptrdi 2 o 4 byte)

Un'altra opzione è quella di utilizzare una struttura di allocazione più semplice di quella malloc()normalmente fornita, come una FreeList , sebbene ciò non consenta di allocare blocchi di dimensioni variabili.


3
Mi chiedo quando malloc sia considerato una cosa positiva in embedded.
Kellenjb,

1
Sono ancora d'accordo sul fatto che non si desidera un'allocazione dinamica nei programmi, come altri hanno già detto, ma questo è un ottimo modo per procedere. Il malloc di terze parti progettato per embedded è di gran lunga la scelta migliore. Evitare la segmentazione è un must. @jobyTaffey Ben scritto.
Kortuk,

1
@Kellenjb, questa è una domanda completamente nuova :-)
Toby Jaffey,

1
Vorrei suggerire che my_free dovrebbe impostare heap_ptr sul valore passato, liberando effettivamente l'elemento indicato e tutto ciò che viene allocato dopo di esso. Ovviamente è necessario allocare le cose in una sequenza che consenta tale utilizzo, ma tali schemi non sono rari. Un'altra utile variante è quella di avere due coppie di funzioni alloc / free, una delle quali alloca dall'alto verso il basso e l'altra alle allocazioni dal basso verso l'alto.
supercat

13

Questa non è una risposta alla tua domanda, ma l'allocazione dinamica della memoria è generalmente disapprovata in piccoli ambienti RAM e in assenza di un sistema operativo (ad esempio nel mondo dei microcontrollori) ... lo spazio heap che hai a disposizione in un ambiente incorporato è in genere misurato in centinaia di byte ...

L'implementazione di malloc e free è essenzialmente il mantenimento di un elenco collegato di strutture di "segmenti liberi" e, come puoi immaginare, i metadati associati ai segmenti liberi non sono privi di sostanza se confrontati con la quantità di memoria normalmente disponibile ... che è il "sovraccarico" "della gestione di un pool di memoria dinamica consuma una quantità significativa delle risorse disponibili.


Esistono implementazioni in cui l'overhead dei metadati è molto piccolo. Per un blocco allocato, hai solo bisogno delle sue dimensioni. Per i blocchi non utilizzati, i puntatori dell'elenco collegato di solito possono adattarsi gratuitamente, anche con dimensioni minime dei blocchi abbastanza ragionevoli.
Ripristina Monica il

Il problema con i sistemi piccoli e di lunga durata che utilizzano microcontrollori non riguarda in genere i metadati, ma la frammentazione della memoria. Quel che è peggio, piccole modifiche al codice possono introdurre una frammentazione della memoria che prima non esisteva, in modo da poter apportare una modifica dall'aspetto innocente che improvvisamente fa smettere il programma di funzionare "troppo presto".
Ripristina Monica il

11

Non so se la libreria standard C18 supporta malloce free, ma la nota sull'app Microchip AN914 mostra come è possibile implementare la propria.

In ogni caso, Thomas e altri manifesti hanno suggerito che l'uso della memoria dinamica sui PIC con il loro spazio RAM molto ridotto è pieno di pericoli. Puoi esaurire rapidamente lo spazio contiguo a causa della mancanza di gestori di memoria virtuale più avanzati che ti danno il sistema operativo completo, portando a allocazioni e arresti anomali non riusciti. Peggio ancora, potrebbe non essere deterministico e probabilmente sarà un problema per il debug.

Se ciò che stai facendo è veramente determinato dinamicamente in fase di esecuzione (raro per la maggior parte delle cose incorporate) e hai solo bisogno di allocare spazio in un paio di occasioni molto speciali , potrei vedere malloced freeessere accettabile.


L'esaurimento dello spazio contiguo, noto anche come frammentazione dell'heap, è un problema completamente indipendente dalla dimensione dello spazio di indirizzamento e dalla memoria virtuale. Potresti voler scambiare un po 'di RAM sprecata per una frammentazione dell'heap inferiore, ma alla fine, su un sistema a lunga esecuzione, non hai garanzie di non rimanere a corto di spazio. L'unica differenza tra i sistemi piccoli e grandi qui è quella di quanto tempo impiega il disco a iniziare il thrashing (su sistemi con VM con paging del disco) o all'allocatore per restituire NULL (su roba incorporata).
Ripristina Monica il

@KubaOber: In genere è possibile garantire che una determinata RAM RAM sia in grado di gestire qualsiasi sequenza di allocazione e operazioni di rilascio che non necessitano mai più di una certa quantità (più piccola) di RAM per essere allocate contemporaneamente. Il problema con i sistemi embedded è che il successo garantito anche nel peggiore dei casi richiederà molta più RAM di quanto sarebbe necessario senza frammentazione.
supercat,

@supercat Hai ragione. Ero davvero eccessivamente drammatico. Esistono prove formali di tali garanzie.
Ripristina Monica il

2

Bene, quanto è grande il tuo PIC in termini di memoria?

malloc è un modo molto inefficiente di allocare memoria. Il problema è che la memoria può essere frammentata con frequenti frees e malloc e con solo pochi kilobyte di memoria, gli errori di allocazione sono fin troppo comuni. È abbastanza probabile che se stai usando un chip più piccolo o un PIC18 precedente non c'è supporto per malloc, poiché Microchip lo considerava molto difficile da implementare (o forse addirittura impossibile in alcuni casi) o non abbastanza usato per essere ne e 'valsa la pena. Per non parlare, ma è anche piuttosto lento, stai guardando 1 ciclo per usare un buffer statico già disponibile e da 100 a 1.000 di cicli per fare un malloc.

Se si desidera allocare staticamente, creare cose come un buffer per le funzioni sprintf (se presenti, circa 128 byte), un buffer per la scheda SD (se presente) e così via, fino a quando non si rimuove la necessità di malloc. Idealmente, lo usi solo dove ne hai assolutamente bisogno e non riesci a cavartela con l'allocazione statica, ma queste situazioni sono di solito rare e forse un segno che dovresti guardare microcontrollori più grandi / più potenti.

E se stai sviluppando / porting un "sistema operativo" sul PIC18 e se supporta i microcontrollori probabilmente ha il supporto per l'allocazione statica. Ad esempio, SQLite3 supporta l'allocazione statica: lo si assegna a un array di buffer di grandi dimensioni e lo utilizza laddove possibile, anche se non è per i microcontrollori. In caso contrario, sei sicuro che sia progettato per un piccolo PIC18?


Capisco cosa intendi .. Sto usando PIC18F87J11, che ha 128K di RAM, può essere sufficiente?
stef

Stefano, quel chip ha 3.904 byte di RAM. Ha 128K di memoria flash di programma.
W5VO,

@Stefao Salati - 3.8KB è minuscolo.
Thomas O

Giusto scusa .. pensi che possa essere abbastanza comunque?
stef

@Stefano Salati, non c'è bisogno di scusarsi. Penso che lo spingerai davvero. Potrebbe funzionare, ma ci vorrebbe un bel po 'di prestazioni e la tua memoria libera.
Thomas O

2

Se stai considerando malloc()e free()per il tuo software incorporato ti suggerisco di dare un'occhiata a uC / OS-II e OSMemGet()e OSMemPut(). Mentre malloc()ti consente di allocare un blocco arbitrario di memoria, OSMem*()ti dà un blocco di dimensioni fisse da un pool pre-allocato. Trovo questo approccio un buon equilibrio tra la flessibilità malloc()e la solidità dell'allocazione statica.


0

AFAIK, per farlo correttamente, devi davvero guardare un dispositivo con una sorta di unità di gestione della memoria (MMU). Mentre esistono meccanismi di allocazione dinamica per la serie PIC18, non saranno davvero così solidi - e parlando come qualcuno che ha lavorato sul firmware che spinge i limiti della serie PIC18, posso dire che non otterrai un'applicazione considerevole in là se spendi tutto il sovraccarico in un gestore di memoria.

Soluzione migliore: cerca di capire cosa sta facendo e perché ha bisogno di allocazione dinamica. Verifica se non riesci a ricodificarlo per funzionare con allocazione statica. (Può darsi che ciò non sia semplicemente possibile - se la libreria / applicazione è progettata per fare qualcosa che si ridimensiona liberamente o non ha limiti della quantità di input che può accettare.) Ma a volte, se pensi davvero su ciò che stai cercando di fare, potresti scoprire che è possibile (e forse anche abbastanza semplice) utilizzare l'allocazione statica.


1
Sei inesatto. Una MMU consente di interfacciarsi con la memoria esterna (probabilmente superiore a 4kB sul PIC). C'è poca differenza nell'allocazione dinamica e statica con e senza MMU. Una volta che inizi a entrare nella memoria virtuale, c'è una differenza, ma è solo tangenzialmente correlata a malloc.
Kevin Vermeer,

1
I primi programmatori Macintosh usavano malloc () e free () (o i loro equivalenti Pascal) abbastanza spesso, nonostante il fatto che i primi computer Macintosh non avessero MMU. L'idea che "correttamente" usando malloc () richieda una MMU mi sembra errata.
David
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.