Forum: Mikrocontroller und Digitale Elektronik Compiler & dynamische Speicherverwaltung auf uC


von Laspalmas (Gast)


Lesenswert?

Hallo,

ich möchte den Xml-Parser "mini xml" in meiner Applikation auf einen 
STM32F4-Controller verwenden.
Leider dieser Parser verwendet intensiv die dynamische 
Speicherverwaltung (malloc,free). Ich mach mir aber Sorgen dass 
irgendwann mal der Speicher
gelöschert ist und nichts mehr geht.
Was ist eure Meinung? Hände weg von alloc und free oder eher der 
Compiler Keil/IAR sorgt dafür dass der Speicher aufgeräumt ist?
NB: STM32F4 hat keine MMU!

Gruß
Laspalmas

von Brummbär (Gast)


Lesenswert?

Nur malloc und free wird Dir irgendwann einen schweizer Käse ohne Platz 
für neue Objekte produzieren. Du brauchst zusätzlich eine 
Speicherverwaltung die beizeiten den Speicher defragmentiert.

von Jim M. (turboj)


Lesenswert?

Laspalmas schrieb:
> ich möchte den Xml-Parser "mini xml" in meiner Applikation auf einen
> STM32F4-Controller verwenden.

Nein, möchtest Du nicht. Du möchtest das an der Stelle wo es anfällt in 
ein anderes (z.B. binäres) Format wandeln was man ohne Kopfstände auf 
dem STM32F4 verwenden kann.

Wie bekommt denn der µC das XML File eingeblasen?

von Laspalmas (Gast)


Lesenswert?

Danke für die schnelle Antwort,
D.h.ich muss mich selber um die Defragmentierung kümmern?
Ich denke es ist keine leichte Sache dies zu implementieren oder?.
Andere Ideen vielleicht?
Danke

von Laspalmas (Gast)


Lesenswert?

@Jim Meba
Der uC bekommt XML von einem Steuergerät via der seriellen Schnittstelle 
(Protokoll basiert auf xml)

von Sebastian V. (sebi_s)


Lesenswert?

Das Problem der Fragmentierung hat man nur wenn man ständig Platz für 
neue Objekte reserviert, dann nur einen Teil freigibt und dann wieder 
neuen Platz reserviert. Wenn während des Parsens des XML Blocks der RAM 
reicht und dann wieder alles freigegeben wird bis zum nächsten XML Block 
sollte das schon passen. Den Fall das man einen empfangenen XML Block 
nicht komplett parsen kann weil er viel zu groß ist muss man so oder so 
abfangen, egal ob man nun dynamisch Speicher reserviert oder statisch 
irgendwo was hat.

Wobei es da auch immer auf die Anwendung ankommt. Wenn es irgendwas 
Sicherheitskritisches ist wo Menschenleben davon abhängen ob noch 
genügend Speicher da ist würde ich kein malloc nutzen.

von Falk B. (falk)


Lesenswert?

@ Laspalmas (Gast)

>D.h.ich muss mich selber um die Defragmentierung kümmern?

Jain. Wenn dein malloc was taugt, dann räumt das nach Freigabe von 
Blöcken so gut wie möglich selber wieder auf. Da macht AFAIK sogar das 
malloc im avr gcc.

http://nongnu.org/avr-libc/user-manual/malloc.html

"When calling free(), a new freelist entry will be prepared. An attempt 
is then made to aggregate the new entry with possible adjacent entries, 
yielding a single larger entry available for further allocations. That 
way, the potential for heap fragmentation is hopefully reduced. When 
deallocating the topmost chunk of memory, the size of the heap is 
reduced."

>Ich denke es ist keine leichte Sache dies zu implementieren oder?.

Man darf halt nicht endlos wild Speicher anfordern und freigeben sondern 
alles etwas Piano. Dann ist auch ein malloc auf einem eher großen 
STM32F4 überaus OK. malloc ist nicht böse, man sollte es nur mit Bedacht 
verwenden.

: Bearbeitet durch User
von Stefan F. (Gast)


Lesenswert?

Speicher zub defragmentieren bedeutet, Daten zu verschieben. Dabei 
müssen alle Zeiger auf die Daten angepasst werden. Also brauchst du auch 
eine Lister aller Zeiger auf die Daten.

Das tut man sich nicht freiwillig an. Besser das Programm so gestalten, 
daß es entweder gar keinen dynamischen Speicher benötigt oder daß es den 
Speicher nach Gebrauch wieder in umgekehrter Reihenfolge freigibt. Dann 
enstehen gar keine Löcher im Heap.

von (º°)·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.· (Gast)


Lesenswert?

> halt nicht endlos wild Speicher anfordern und freigeben

Ein embedded System unterscheidet sich u.a. durch die angestrebte
Uptime/Laufzeit von einem Desktop/Server/Whatever.
Da raeumt dann u.U. sogar das Betriebssystem den Schrott weg.

Ein bischen malloc ist wie ein bischen schwanger.

Keiner kann bei einer Laufzeit von z.B. 9 Monaten garantieren,
dass die Fragmentierung nicht doch zuschlaegt und das
ganze verrecken laesst.

Das kannst du in deinem Projekt eigentlich nur umgehen, indem du
nur fuer den XML-Parser malloc einsetzt, und wenn der Parser fertig
ist, malloc wieder in seinen "Ausgangszustand" versetzt.

Moegliche andere Loesungen waeren vorher definierte statische
Buffer fester Laenge die dann bei Bedarf benutzt und wieder
freigegeben werden. Da kann eine Fragmentierung nicht auftreten.
Sowas nennt sich Memorypools.

von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> Speicher zub defragmentieren bedeutet, Daten zu verschieben. Dabei
> müssen alle Zeiger auf die Daten angepasst werden. Also brauchst du auch
> eine Lister aller Zeiger auf die Daten.
>

Das war vielleicht bei Mac OS so. Die gängigen Implementierungen 
verschieben nix, die verschmelzen lediglich nebeneinanderliegende, freie 
Speicherblöcke zu größeren, falls möglich.

Ansonsten finde ich die Furcht vor malloc/free ein wenig übertrieben.

Mindestens zwei Jahrzehnte sind Betriebssysteme ohne MMU ausgekommen, 
ohne daß es ständig gekracht hätte. Wenn's also nicht gerade um 
"lebensgefährlichen" Code geht und man's nicht übertreibt, spricht m.E. 
nichts dagegen.

Es gibt eine ganze Menge Dinge (z.B. USB-Host), wo's ohne dynamische 
Speicherverwaltung nur mit ekligen Klimmzügen vorwärts geht.

von Stefan F. (Gast)


Lesenswert?

> die verschmelzen lediglich nebeneinanderliegende, freie
> Speicherblöcke zu größeren, falls möglich.

Das mag sein, es ist dann aber keine Defragmentierung.

> Mindestens zwei Jahrzehnte sind Betriebssysteme ohne MMU
> ausgekommen, ohne daß es ständig gekracht hätte.

Hier geht es aber um Mikrocontroller ohne MMU.

Abgesehen davon ist mir bekannt, daß Windows selbst schon lange kein 
problem mit fragmentiertem Heap hat. Aber ich habe einige fehlerhafte 
Anwendungsprogramme gesehen, die von dem Problem betroffen waren. 
Windows kann auch nicht alles glatt bügeln.

> Ansonsten finde ich die Furcht vor malloc/free ein wenig übertrieben.

Dann schau Dir mal Threads an, wo Leute Speicherprobleme mit Arduino 
haben. Da ist fragmentierter Heap einer der häufigsten Probleme. Die 
String Klasse verleitet ja auch gerade dazu, es falsch zu nutzen.

von Markus F. (mfro)


Lesenswert?

Stefan U. schrieb:
> Windows kann auch nicht alles glatt bügeln.

Windows? Windows ist meist nicht die Behebung, sondern die Ursache!

von Laspalmas (Gast)


Lesenswert?

Danke sehr für die zahlreiche Teilnahme an diese Diskussion.

Das mit dem memorypool finde ich eine gute Idee. Aber ich verstehe es 
nicht ganz. Laut Wikipedia:
".... A more efficient solution is preallocating a number of memory 
blocks with the same size called the memory pool. The application can 
allocate, access, and free blocks represented by handles at run 
time...".
D.h. egal wie viel Bytes alloc fordert, wird immer eine feste Größe 
zurückgegben? Was ist wenn der geforderte Speicher für das XML-Objekt 
größer als "the size of the memory pool" ist? wird malloc dann an diese 
Stelle scheitern? oder bekommt sie ein vielfaches von "memory pool 
size"? dann sind wir wieder beim gleichen Problem: Fragmentierter 
Speicher oder?.


Andere Lösung was ich in diesem Forum gefunden habe, ist "alloca" welche 
den Stack-Speicher der Task verwendet und nicht den Heap-Speicher. Hat 
jemand gute Erfahrung mit "alloca" in Zusammenhang mit RTOS (Keil RTX)? 
wie gross soll der Stack sein für jeden Task (Faustregel)? problematisch 
wird es bei rekursive Funktionen, die alloca verwenden.

Gruß
Laspalmas

von Falk B. (falk)


Lesenswert?

@Laspalmas (Gast)

>".... A more efficient solution is preallocating a number of memory
>blocks with the same size called the memory pool. The application can
>allocate, access, and free blocks represented by handles at run
>time...".
>D.h. egal wie viel Bytes alloc fordert, wird immer eine feste Größe
>zurückgegben?

Nein, das bedeutet, daß man eine mehr oder weniger einfache, zusätzliche 
Verwaltung auf malloc & Co draufsetzt.

>size"? dann sind wir wieder beim gleichen Problem: Fragmentierter
>Speicher oder?.

Mach dir mal nicht zuviel Gedanken. Nutze diesen XML Parser und teste.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Laspalmas schrieb:
> D.h. egal wie viel Bytes alloc fordert, wird immer eine feste Größe
> zurückgegben?

Nein. Du musst {c|m}alloc so aufrufen, daß Du mit einer sinnvollen fixen 
Größe arbeitest. Der "memory pool" besteht aus dem Resultat mehrerer 
dieser Aufrufe, die Verwaltung davon musst Du dann selbst übernehmen.

Und nein, wenn Speicher für Objekte angefordert werden soll, die größer 
sind als die fixe Größe, dann funktioniert das nicht.


Ich halte die Idee, XML auf einem µC zu verarbeiten, für einen 
Designfehler.
Nimm wenigstens JSON, da ist der Wasserkopf nicht ganz so groß. Dafür 
gibt es sogar minimalistische C-Implementierungen, die ohne dynamische 
Speicherverwaltung auskommen, wie z.B. 
http://www.catb.org/esr/microjson/

von Karl (Gast)


Lesenswert?

Der von alloca zurückgegebene Zeiger ist aber auch nur so lange gültig 
wie der Stack, auf dem er angelegt wurde.

Btw. mache ich was ganz ähnliches. XML aus einer Config-Datei einlesen 
und damit meine Objektstruktur(en) aufbauen. (Jaja, mit pöhhhsem C++ auf 
einem Controller. Pure virtual wird auch verwendet ;-P) Die im XML 
konfigurierten Objekte werden dynamisch angelegt. Mit new.

Allerdings verwende ich die newlib. Und da muss man den Allocator für 
eine "Page" sowieso selbst schreiben. malloc fordert nämlich immer 4 kiB 
an, die dann intern verwaltet werden. Das Gute daran: Man kann sehr gut 
kontrollieren, wieviel Speicher malloc nun unter seiner Fuchtel hat. Und 
siehe da: Keinerlei Fragmentierung. Voraussetzung ist aber, wie schon 
mehrmals angemerkt, dass man zwischendurch alles wieder freigibt. 
Objekte, die dauerhaft benötigt werden braucht man aber auch nicht 
dynamisch zu allozieren.

In Deinem konkreten Fall würde ich raten: Probier es gewissenhaft aus. 
Die Angst vor malloc/new ist da (wie auch C++, pure virtual, exceptions) 
und in sicherheitsrelevanten Anwendungen sicher doppelt und dreifach 
gerechtfertigt, kann aber auf einem Cortex M4 sicher überwunden werden.

von Ruediger A. (Firma: keine) (rac)


Lesenswert?

Jim M. schrieb:
> Laspalmas schrieb:
>> ich möchte den Xml-Parser "mini xml" in meiner Applikation auf einen
>> STM32F4-Controller verwenden.
>
> Nein, möchtest Du nicht. Du möchtest das an der Stelle wo es anfällt in
> ein anderes (z.B. binäres) Format wandeln was man ohne Kopfstände auf
> dem STM32F4 verwenden kann.
>

Full ACK! Für Ressourcenbeschränkte Systeme sollte ein M2M Protokoll 
regulär beschrieben werden können. XML ist so ungefähr die 
allerschlechteste Wahl, weil es eine doppelt rekursiv definierte Sprache 
ist. Du kannst noch so eine gute dynamische Speicherverwaltung haben, 
aber wenn Du die Rekursionstiefe nicht beschränkst, wird Dir ab einer 
vom Gesamtressourcebedarf abhängigen Verschachtelungstiefe 
notwendigerweise der Stack oder (falls Du einen endrekursiven Parser 
hast) der Heap platzen.

> Wie bekommt denn der µC das XML File eingeblasen?

Ja, das ist die Andere Frage. Effizientes XML sollte In Memory geparst 
werden, aber da XML Dateien durch Whitespace mehrere Größenordnungen 
über notwendigem Bedarf aufgebläht sein können, ist es auch 
unrealistisch, die gesamte Datei im Speicher zwischen zu halten. Also 
als Stream, aber da hast Du das Problem, dass die Länge des Streams erst 
bekannt ist, wenn der gesamte Main Tag geparst ist. Auch das mögen 
ressourcenbeschränkte Systeme überhaupt nicht.

Fast jedes Andere M2M Protokoll ist besser für uCs geeignet als XML.

: Bearbeitet durch User
von Laspalmas (Gast)


Lesenswert?

Danke @Rufus für die schnelle Antwort.

leider es liegt nicht in meiner Hand xml oder json zu verwenden.
Die Ansteuerung ist festgelegt und kommt in xml-format via der seriellen 
Schnittstelle.
Vielleicht gibt es XML-Parser welche ohne dynamische Speicherverwaltung 
auskommen. Kennt jemand einen?

Gruß
Laspalmas

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Laspalmas schrieb:
> Die Ansteuerung ist festgelegt und kommt in xml-format via der seriellen
> Schnittstelle.

Und die kann völlig frei alle Möglichkeiten des XML ausnutzen, oder ist, 
durch die Anwendung mit Deinem Controller, nicht eine gewisse starrere 
Variante zu erwarten?

Wieviele unterschiedliche Parameter werden denn da übertragen?

Vielleicht ist ein vollständiger XML-Parser hier kompletter Overkill, 
und es würde genügen, mit simpleren Methoden vorzugehen und nur die 
Deinen µC tatsächlich interessierenden Informationen zu extrahieren.

Zeig' doch mal so einen XML-Datensatz, den Du verarbeiten sollst.

von Laspalmas (Gast)


Lesenswert?

Hallo,

der xml Part ist vom Kunde spezifiziert und darf ich leider nicht 
veröffentlichen. Auf jeden Fall es ist ohne DTD und wird als well formed
ankommen (als Baum-Struktur).Die Anzahl und Größe der Objekte sind nicht 
vorhersehbar: mal ein Objekt mal mehrere Objekte mit unterschiedlichen 
Größen. Die Gegenseite erwartet auch Antworten die auch xml basierend 
ist. Also generieren von xml-Antwort ist ein muss. Dafür könnte 
"sprintf" ausreichen (Elegant ist es aber nicht:(.
Das Parsen mit "strstr" ist mühsam, daher suche ich einen xml Parser, 
der mir die Sachen leichter macht.

Gruß

von Hans-Georg L. (h-g-l)


Lesenswert?

Nimm einen nicht validierenden SAX (Simple Api for XML) und keinen DOM 
Parser. Mir ist im Moment allerdings keine fertige embedded Version in C 
bekannt. Es gibt aber den Nano XML Parser in Java und vielleicht kann 
man den SAX Parser davon auf C umschreiben.

von Karl (Gast)


Lesenswert?

Auch wenn es langsam vom Thema der dynmaischen Speicherverwaltung 
abkommt...

Laspalmas schrieb:
> Die Anzahl und Größe der Objekte sind nicht
> vorhersehbar: mal ein Objekt mal mehrere Objekte mit unterschiedlichen
> Größen.
Ja, aber die Art der Objekte, Verschachtelungstiefe und die maximale 
Anzahl wird doch begrenzt sein. Bei mir läuft ein selbst geschriebener 
Tokenizer und ein Parser. Sicher nicht das schönste Stück Software auf 
der Welt, aber gemessen am Aufwand bin ich zufrieden. Es gibt ja nicht 
immer Echtzeit für alles im µC.
Wie schon geschrieben wird man davon ausgehen müssen oder können, dass 
das ankommende XML korrekt ist.

> Die Gegenseite erwartet auch Antworten die auch xml basierend
> ist. Also generieren von xml-Antwort ist ein muss. Dafür könnte
> "sprintf" ausreichen (Elegant ist es aber nicht:(.
Da bin ich ganz anderer Ansicht. Elegant ist was den Job mit 
erträglichem Aufwand erledigt. Je nach Komplexität der Antwort fände ich 
ein paar sprintf besser als ein DOM. Weil: Einfach und 
resourcenschonend.

von Laspalmas (Gast)


Lesenswert?

Karl schrieb:
> Bei mir läuft ein selbst geschriebener
> Tokenizer und ein Parser.




Kannst du den source code posten? vielleicht hilft es.

Gruß
Laspalmas

von J. F. (Firma: Père Lachaise) (rect)


Lesenswert?

Ich hatte Anfang des Jahres exakt das gleiche Problem: bin mit "mini 
xml" gestartet und mir war schnell klar das die nix taugt (auf Grund der 
Thematik die hier eingängig beschrieben worde). Ich habe mir am Ende 
einen eigenen Parser geschrieben der ohne dynamische Speicherallokierung 
auskommt. Evtl. machst du das?

von Laspalmas (Gast)


Lesenswert?

@ J. Fourier

Für mich lieber wenn ich auf bestehende Lösungen zugreifen konnte.
Es nimmt viel Zeit in Anspruch ein eigener Parser zu schreiben und vor 
allem viel viel testen.

Wahrscheinlich komme ich nicht drum herum.

Danke
Laspalmas

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Rufus Τ. F. schrieb:
> imm wenigstens JSON, da ist der Wasserkopf nicht ganz so groß. Dafür
> gibt es sogar minimalistische C-Implementierungen, die ohne dynamische
> Speicherverwaltung auskommen, wie z.B.

Jap exakt, oder diese Library:

https://github.com/bblanchon/ArduinoJson

Die hat mit Arduino nichts zu tun, ich hatte sie auf einem STM32 mit C++ 
einfach verwendet.

Das nette ist, man kann einen statischen Buffer bereitstellen und nur in 
dem wird dann gearbeitet.

Beispiel von der Seite:
1
char json[] = "{\"sensor\":\"gps\",\"time\":1351824120,\"data\":[48.756080,2.302038]}";
2
3
StaticJsonBuffer<200> jsonBuffer;
4
5
JsonObject& root = jsonBuffer.parseObject(json);
6
7
const char* sensor = root["sensor"];
8
long time          = root["time"];
9
double latitude    = root["data"][0];
10
double longitude   = root["data"][1];

Hab die Lib für die Kommunikation mit einem in JQuery-geschriebenen 
Webfrontend im Einsatz und das läuft seit Wochen ohne Probleme.

von J. F. (Firma: Père Lachaise) (rect)


Lesenswert?

Laspalmas schrieb:
> Es nimmt viel Zeit in Anspruch ein eigener Parser zu schreiben und vor
> allem viel viel testen.

Stimme ich absolut zu. Mein Parser war als Folge auch stark angepasst 
auf das entsprechende Konfigurationsfile. Als Idee kannst du dir ja auch 
die Quellen von "mini xml" anschauen, die sind ja frei einsehbar.

Viel Erfolg!

von Laspalmas (Gast)


Lesenswert?

Ja ich gebe euch recht. JSON ist die bessere Auswahl für den 
Datenaustausch mit einm uC. Aber XML ist eine Vorgabe hier: Ein muss.
Danke euch für wertvolle Infos. Mit Sicherheit sind die gelieferten 
Infos nützlich für anderen in diesem Forum.

von Mampf F. (mampf) Benutzerseite


Lesenswert?

Laspalmas schrieb:
> Aber XML ist eine Vorgabe hier: Ein muss.

Wie komplex ist das XML? Hast du eine Beispieldatei?

von Laspalmas (Gast)


Lesenswert?

Laspalmas schrieb:
> Hallo,
>
> der xml Part ist vom Kunde spezifiziert und darf ich leider nicht
> veröffentlichen. Auf jeden Fall es ist ohne DTD und wird als well formed
> ankommen (als Baum-Struktur).Die Anzahl und Größe der Objekte sind nicht
> vorhersehbar: mal ein Objekt mal mehrere Objekte mit unterschiedlichen
> Größen. Die Gegenseite erwartet auch Antworten die auch xml basierend
>
> Gruß

von Karl (Gast)


Lesenswert?

Laspalmas schrieb:
> Kannst du den source code posten? vielleicht hilft es.

Nein, geht nicht. Ist aber nichts wildes

Tokenizer: Suche ausgehend vom Startpunkt nach Starttoken, kopiere alles 
bis zum Endtoken und gib das zurück.

Parser:
1
Wenn Token == TokenX, mach Aktion
2
Wenn Token == TokenY, hole nächsen Token
3
     Wenn Token == Token Z hole Text und mach was damit
4
...

von (º°)·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.·´¯`·.¸¸.· (Gast)


Lesenswert?

Falk Brunner schrieb:
> Nein, das bedeutet, daß man eine mehr oder weniger einfache, zusätzliche
> Verwaltung auf malloc & Co draufsetzt.

Memorypools brauchen kein malloc.
Die werden ueblicherweise statisch konfiguriert.
Aenderungen an der Konfiguration der Memorypools erfordern
in der Regel einen Neustart des Systems.

> D.h. egal wie viel Bytes alloc fordert, wird immer eine feste Größe
> zurückgegben?

Ein normales alloc ist dafuer nicht gedacht.
Wenn ein Speicher einer bestimmten Groesse gesucht wird,
wird der aus dem Bestand des Pools entnommen.
U.U. wird der naechst groessere Poolmember ausgewaehlt.

Ueblicherweise gibt es unterschiedliche Speichergroessen die im
Memorypool organisiert werden:
- small
- medium
- large
- very large

Die Anzahl und die Groesse jeder Klasse ist fix, kann aber
natuerlich bei Bedarf auch parametriert werden.

Nachteile gibt es natuerlich auch, weil u.U. ein groesserer
Poolmember als benoetigt die Anforderung realisiert.

Nur: Fragmentieren kann so ein Konstrukt nie.

Memorypools sind nicht dafuer gedacht, statische Daten
abzulegen.

von Duenner Troll (Gast)


Lesenswert?

Ein Serielles Protokoll verlangt einen Ringuffer, oder einen linearen 
Buffer. Ich wuerd das XML auch gleich aus diesem heraus parsen. Wozu 
sollte ich zusaetzlichen Speicher anfordern ?

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.