Verwendung von Malloc in PIC

10

Wie kann ich einen PIC verwenden malloc()und free()funktionieren? Ich habe den stdlib.hHeader überprüft und es werden sie nicht erwähnt. Ich benutze MCC18.

Musste jemand sie benutzen?

Ich brauche sie, weil ich eine Bibliothek von Windows XP auf den PIC portiere. Die Portierungsanleitung sagt zu

Passen Sie die spezifischen Funktionen des Betriebssystems an meine PIC-Funktionen an

Aber ich weiß nicht, wie ich die malloc()und free()Funktionen "übersetzen" soll .

stef
quelle
4
Versuchen Sie nach Möglichkeit, die statische Zuordnung zu verwenden.
Nick T
1
Warum? Das Problem ist wirklich, dass ich die unterste Ebene (die plattformspezifische) einer ziemlich großen Bibliothek schreibe und viele Funktionen, für die ich keine Ahnung habe, wofür sie verwendet werden. Und ich habe keine Ahnung, wie ich mich ändern soll dynamisch bis statisch ..
stef
11
Es klingt so, als ob ein PIC-Mikrocontroller mit <4 KB RAM für Ihre Anwendung falsch sein könnte. Messen Sie auf einem PC die Speichernutzung Ihrer PC-Bibliothek, bevor Sie einen Port starten. Mit etwas Stärkerem wie einem ARM Cortex-M3 sind Sie vielleicht besser dran. Faustregel: Wenn die Codebasis, die Sie portieren, zu groß ist, um sie zu verstehen, passt sie nicht in einen PIC.
Toby Jaffey
Windows-Treiber (und Anwendungen im Allgemeinen) werden im Wesentlichen mit einem "unbegrenzten RAM" -Paradigma geschrieben, da bei Erschöpfung des physischen RAM virtueller Speicher ausgetauscht werden kann. Je nachdem, was die Bibliothek tut, verbraucht sie möglicherweise mehr als 4 KB verfügbar in Ihrem PIC18F87J11. Ich vermute, dass Sie nicht abschätzen können, wie viel Speicher der Treiber verwenden wird.
Adam Lawrence
Ein weiteres potenzielles Problem: Ein Win32-Int ist 32 Bit, während es beim MCC18-Compiler nur 16 Bit sind. Sie könnten seltsame Überlaufprobleme bekommen, wenn Sie nicht vorsichtig sind.
Adam Lawrence

Antworten:

8

In vielen Anwendungen muss Speicher zugewiesen werden, es muss jedoch nichts freigegeben werden, während etwas beibehalten wird, das danach zugewiesen wurde. Auf einem solchen System muss lediglich der Linker verwendet werden, um ein Array mit dem gesamten verfügbaren RAM zu definieren, einen Zeiger auf den Anfang dieses Arrays zu setzen und dann eine nette einfache Malloc-Funktion zu verwenden:

char * next_alloc;
void * malloc (int size)
{
    char * this_alloc;
    this_alloc = next_alloc;
    if ((END_OF_ALLOC_SPACE - this_alloc) <Größe)
      return -1;
    next_alloc + = Größe;
    return this_alloc;
}}
nichtig frei (nichtig * ptr)
{
    if (ptr)
        next_alloc = (char *) ptr;
}}

Schön und einfach, und nur zwei Bytes Gesamtaufwand für eine beliebige Anzahl von Zuordnungen. Wenn Sie free () für einen Block aufrufen, wird die Zuordnung dieses Blocks und aller darauf folgenden Elemente aufgehoben.

Etwas kompliziertere Zuordnungsmuster können mithilfe von zwei Zeigern behandelt werden - einer, der Daten von der Unterseite des Speichers nach oben und eine von der Oberseite des Speichers nach unten zuordnet. Es ist auch möglich, einen komprimierenden Garbage Collector zu verwenden, wenn die Daten im Heap homogen sind und man weiß, wo sich alle externen Verweise darauf befinden.

Superkatze
quelle
Um vollständig über die Möglichkeiten informiert zu sein, lesen Sie die Antwort unter electronic.stackexchange.com/questions/7850/…
gavioto20
14

malloc()in Mikrocontrollern wird allgemein als "schlechte Sache" angesehen. Wenn Sie es jedoch unbedingt benötigen, sollten Sie eine Version eines Drittanbieters suchen.

Wenn Sie Glück haben, hängt der zu portierende Code möglicherweise nicht von der Wiederverwendung von Speicherblöcken ab. In diesem Fall können Sie einen einfachen Allokator schreiben, der einen Zeiger in einen RAM-Puffer zurückgibt und den Zeiger dann um die angeforderte Blockgröße vorschiebt.

Ich habe diesen Ansatz bereits erfolgreich beim Portieren von PC-Bibliotheken auf Mikrocontroller verwendet.

Im Folgenden würden Sie den Allokator mit einrichten my_malloc_init()und Speicher mit zuweisen my_malloc(). my_free()ist da, um die Abhängigkeit zu befriedigen, wird aber eigentlich nichts tun. Irgendwann wird Ihnen natürlich der Platz ausgehen.

Damit dies funktioniert, müssen Sie den Speicherbedarf Ihres Codes im ungünstigsten Fall messen (dies möglichst auf einem PC tun) und dann entsprechend einrichten HEAP_SIZE. Rufen Sie an, bevor Sie den Teil Ihrer Bibliothek betreten, der dynamischen Speicher benötigt my_malloc_init(). Stellen Sie vor der Wiederverwendung sicher, dass noch nichts darauf zeigt 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
}

(Hinweis: In der realen Welt müssen Sie möglicherweise die Zeigerausrichtung berücksichtigen, dh das Aufrunden heap_ptrum 2 oder 4 Byte.)

Eine andere Möglichkeit besteht darin, eine einfachere Zuordnungsstruktur als malloc()gewöhnlich zu verwenden, wie z. B. eine FreeList . Auf diese Weise können Sie jedoch möglicherweise keine Blöcke mit variabler Größe zuweisen.

Toby Jaffey
quelle
3
Ich frage mich, wann Malloc als eine gute Sache in Embedded angesehen wird.
Kellenjb
1
Ich stimme immer noch zu, dass Sie keine dynamische Zuweisung in Programmen wünschen, wie andere gesagt haben, aber dies ist ein guter Weg, um dies zu erreichen. Malloc von Drittanbietern für Embedded ist bei weitem die beste Wahl. Das Vermeiden von Segmentierungen ist ein Muss. @jobyTaffey Gut geschrieben.
Kortuk
1
@Kellenjb gut, das ist eine ganz neue Frage :-)
Toby Jaffey
1
Ich würde vorschlagen, dass my_free heap_ptr auf den übergebenen Wert setzt, um das angegebene Element und alles, was danach zugewiesen wird, effektiv freizugeben. Natürlich muss man Dinge in einer Reihenfolge zuordnen, die eine solche Verwendung erlaubt, aber solche Muster sind nicht ungewöhnlich. Eine weitere nützliche Variante besteht darin, zwei Paare von Zuweisungs- / freien Funktionen zu haben, von denen eines von oben nach unten und das andere von unten nach oben zuweist.
Supercat
13

Dies ist kaum eine Antwort auf Ihre Frage, aber die dynamische Speicherzuweisung wird in kleinen RAM-Umgebungen und in Abwesenheit eines Betriebssystems (z. B. in der Welt der Mikrocontroller) im Allgemeinen verpönt. Der in einer eingebetteten Umgebung verfügbare Heap-Speicherplatz ist normalerweise gemessen in Hunderten von Bytes ...

Das Implementieren von malloc und free ist im Wesentlichen die Pflege einer verknüpften Liste von "freien Segment" -Strukturen, und wie Sie sich vorstellen können, sind die mit freien Segmenten verknüpften Metadaten im Vergleich zu der normalerweise verfügbaren Speichermenge nicht unwesentlich ... das ist der "Overhead" "Das Verwalten eines dynamischen Speicherpools verbraucht einen erheblichen Teil der verfügbaren Ressourcen.

vicatcu
quelle
Es gibt Implementierungen, bei denen der Metadaten-Overhead sehr gering ist. Für einen zugewiesenen Block benötigen Sie nur seine Größe. Für nicht verwendete Blöcke können die verknüpften Listenzeiger normalerweise kostenlos passen, selbst bei recht vernünftigen minimalen Blockgrößen.
Stellen Sie Monica
Das Problem bei kleinen, lang laufenden Systemen, die Mikrocontroller verwenden, besteht normalerweise nicht in Metadaten, sondern in der Speicherfragmentierung. Was noch schlimmer ist: Kleine Änderungen an Ihrem Code können zu einer Speicherfragmentierung führen, bei der es zuvor keine gab, sodass Sie möglicherweise eine unschuldig aussehende Änderung vornehmen, die Ihr Programm plötzlich "viel zu früh" zum Stillstand bringt.
Stellen Sie Monica
11

Ich weiß nicht, ob die C18-Standardbibliothek mallocund unterstützt free, aber der Microchip App Note AN914 zeigt, wie Sie Ihre eigenen implementieren können.

Auf jeden Fall haben Thomas und andere Poster vorgeschlagen, dass die Verwendung von dynamischem Speicher auf PICs mit ihrem sehr kleinen RAM-Speicher mit Gefahren behaftet ist. Aufgrund des Mangels an fortschrittlicheren virtuellen Speichermanagern , die Ihnen von vollwertigen Betriebssystemen zur Verfügung gestellt werden, kann schnell der zusammenhängende Speicherplatz ausgehen , was zu fehlgeschlagenen Zuordnungen und Abstürzen führt. Schlimmer noch, es ist möglicherweise nicht deterministisch und wird wahrscheinlich ein Problem beim Debuggen sein.

Wenn das, was Sie tun, zur Laufzeit wirklich dynamisch bestimmt wird (selten für die meisten eingebetteten Dinge) und Sie nur zu ganz besonderen Anlässen Speicherplatz zuweisen müssen , könnte ich sehen mallocund freeakzeptabel sein.

Nick T.
quelle
Der Mangel an zusammenhängendem Speicherplatz, auch bekannt als Heap-Fragmentierung, ist ein Problem, das völlig unabhängig davon ist, wie groß Ihr Adressraum ist und ob Sie über virtuellen Speicher verfügen. Möglicherweise möchten Sie etwas verschwendetes RAM gegen eine geringere Heap-Fragmentierung austauschen, aber auf einem System mit langer Laufzeit haben Sie möglicherweise keine Garantie dafür, dass nicht genügend Heap-Speicherplatz zur Verfügung steht. Der einzige Unterschied zwischen kleinen und großen Systemen besteht darin, wie lange es dauert, bis die Festplatte mit dem Thrashing beginnt (auf Systemen mit VM auf der Festplatte) oder der Allokator NULL zurückgibt (auf eingebetteten Daten).
Stellen Sie Monica
@KubaOber: Es ist im Allgemeinen möglich zu garantieren, dass ein RAM mit einer bestimmten Größe eine beliebige Folge von Zuweisungs- und Freigabevorgängen verarbeiten kann, für die niemals mehr als eine bestimmte (kleinere) RAM-Menge gleichzeitig zugewiesen werden muss. Das Problem bei eingebetteten Systemen besteht darin, dass für die Gewährleistung des Erfolgs auch im schlimmsten Fall viel mehr RAM erforderlich ist, als dies ohne Fragmentierung erforderlich wäre.
Supercat
@ Supercat Du hast recht. Ich war in der Tat übereifrig dramatisch. Es gibt formelle Beweise für diese Garantien.
Stellen Sie Monica am
2

Wie groß ist Ihr PIC in Bezug auf den Speicher?

malloc ist eine sehr ineffiziente Methode zum Zuweisen von Speicher. Das Problem dabei ist, dass der Speicher mit häufigen Freigaben und Mallocs fragmentiert werden kann und mit nur wenigen Kilobyte Speicher allzu häufig Zuordnungsfehler auftreten. Es ist sehr wahrscheinlich, dass bei Verwendung eines kleineren Chips oder eines früheren PIC18 Malloc nicht unterstützt wird, da Microchip die Implementierung entweder als sehr schwierig (oder in einigen Fällen sogar als unmöglich) ansah oder nicht ausreichend verwendet wurde es ist es wert. Ganz zu schweigen davon, aber es ist auch ziemlich langsam. Sie betrachten 1 Zyklus, um einen bereits verfügbaren statischen Puffer zu verwenden, und 100 bis 1000 Zyklen, um ein Malloc zu erstellen.

Wenn Sie statisch zuordnen möchten, erstellen Sie beispielsweise einen Puffer für Ihre Sprintf-Funktionen (falls vorhanden, ca. 128 Byte), einen Puffer für Ihre SD-Karte (falls vorhanden) usw., bis Sie Malloc nicht mehr benötigen. Im Idealfall verwenden Sie es nur dort, wo Sie es unbedingt benötigen und mit statischer Zuordnung nicht durchkommen können. Diese Situationen sind jedoch normalerweise selten und möglicherweise ein Zeichen dafür, dass Sie sich größere / leistungsstärkere Mikrocontroller ansehen sollten.

Und wenn Sie ein "Betriebssystem" auf dem PIC18 entwickeln / portieren und wenn es Mikrocontroller unterstützt, unterstützt es wahrscheinlich die statische Zuordnung. Beispielsweise unterstützt SQLite3 die statische Zuweisung - Sie weisen ihm ein großes Pufferarray zu und es verwendet dieses, wo dies möglich ist, auch wenn es nicht für Mikrocontroller geeignet ist. Wenn nicht, sind Sie sicher, dass es für einen kleinen PIC18 ausgelegt ist?

Thomas O.
quelle
Ich verstehe, was du meinst. Ich verwende den PIC18F87J11 mit 128 KB RAM. Kann das reichen?
stef
Stefano, dieser Chip hat 3.904 Bytes RAM. Es verfügt über 128 KB Programm-Flash-Speicher.
W5VO
@ Stefao Salati - 3,8 KB ist winzig.
Thomas O
Richtig, tut mir leid. Glaubst du, es kann sowieso genug sein?
stef
@ Stefano Salati, keine Notwendigkeit, sich zu entschuldigen. Ich denke, du würdest es wirklich vorantreiben. Es könnte funktionieren, aber es würde einen Teil der Leistung und Ihres freien Speichers kosten.
Thomas O
2

Wenn Sie überlegen, malloc()und free()für Ihre eingebettete Software schlage ich vor, dass Sie sich uC / OS-II und OSMemGet()und ansehen OSMemPut(). Während malloc()Sie einen beliebigen Speicherblock zuweisen können, erhalten OSMem*()Sie einen Block mit fester Größe aus einem vorab zugewiesenen Pool. Ich finde diesen Ansatz ein gutes Gleichgewicht zwischen der Flexibilität malloc()und der Robustheit der statischen Zuordnung.

trondd
quelle
0

AFAIK, um dies richtig zu machen, müssen Sie sich wirklich ein Gerät mit einer Art Speicherverwaltungseinheit (MMU) ansehen. Es gibt zwar dynamische Zuweisungsmechanismen für die PIC18-Serie, aber sie werden nicht wirklich so solide sein - und als jemand, der an Firmware gearbeitet hat, die die Grenzen der PIC18-Serie überschreitet, kann ich sagen, dass Sie das nicht bekommen werden Eine umfangreiche Anwendung, wenn Sie den gesamten Aufwand für einen Speichermanager aufwenden.

Bessere Lösung: Versuchen Sie zu verstehen, was es tut und warum es eine dynamische Zuordnung benötigt. Überprüfen Sie, ob Sie es nicht neu faktorisieren können, um mit der statischen Zuordnung zu arbeiten. (Es kann vorkommen, dass dies einfach nicht möglich ist - wenn die Bibliothek / Anwendung so konzipiert ist, dass sie frei skalierbar ist oder keine Grenzen für die Menge an Eingaben hat, die sie akzeptieren kann.) Aber manchmal, wenn Sie wirklich nachdenken In Bezug auf das, was Sie versuchen, ist es möglicherweise möglich (und möglicherweise sogar recht einfach), stattdessen die statische Zuordnung zu verwenden.

andersop
quelle
1
Du bist falsch. Mit einer MMU können Sie eine Schnittstelle zum externen Speicher herstellen (wahrscheinlich mehr als 4 KB auf dem PIC). Die dynamische und statische Zuordnung mit und ohne MMU unterscheidet sich kaum. Sobald Sie anfangen, in den virtuellen Speicher zu gelangen, gibt es einen Unterschied, der jedoch nur tangential mit malloc zusammenhängt.
Kevin Vermeer
1
Frühe Macintosh-Programmierer verwendeten ziemlich oft malloc () und free () (oder ihre Pascal-Entsprechungen), obwohl frühe Macintosh-Computer keine MMU hatten. Die Idee, dass "richtig" mit malloc () eine MMU erforderlich ist, scheint mir falsch.
Davidcary