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
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.
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?
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
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.
@ 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.
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.
> 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.
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.
> 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.
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
@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.
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/
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.
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.
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
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.
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ß
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.
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.
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?
@ 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
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:
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!
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.
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ß
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
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.
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 ?