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
@Jim Meba Der uC bekommt XML von einem Steuergerät via der seriellen Schnittstelle (Protokoll basiert auf xml)
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.
:
Bearbeitet durch User
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.
Stefan U. schrieb: > Windows kann auch nicht alles glatt bügeln. Windows? Windows ist meist nicht die Behebung, sondern die Ursache!
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.
:
Bearbeitet durch User
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.
Karl schrieb: > Bei mir läuft ein selbst geschriebener > Tokenizer und ein Parser. Kannst du den source code posten? vielleicht hilft es. Gruß Laspalmas
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:
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.
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: > Aber XML ist eine Vorgabe hier: Ein muss. Wie komplex ist das XML? Hast du eine Beispieldatei?
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 |
4 | ... |
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 ?
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.