PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C++ Embedded - Speicherprobleme...?



DoNuT
04-03-2008, 14:37
Hi leute,

jo, es ist mein erster post hier, und gleich mit nem ganz dicken problem...

und zwar (soviel kann ich wohl sagen), wir entwickeln software für ein embedded-gerät mit nem arm-prozessor und nicht allzuviel speicher, hier treten aber ein paar probleme auf...

ich versuch mal, das grob zu umreißen...

ein großer teil der funktionalität unserer anwendung ist, von dem gerät (okay, ein RFID-reader) daten zu akquirieren, daraus nen fette xml-textmessage zu machen und diese periodisch an einen beliebigen abnehmer übers netzwerk rauszusenden... ist dieser abnehmer nicht verfügbar, puffern wir die messages bis zu einem bestimmten grad (z.b. 1200 kb freier RAM), darunter hören wir auf, da man nur mehr den stecker am gerät ziehen kann, wenn der speicher volläuft...

verbindet sich der abnehmer, sollten die gepufferten nachrichten eigentlich rausgeblasen werden und würden uns erwarten, dass der speicherverbrauch im großen und ganzen auf das niveau "vor dem sturm" zurückginge, was eben nicht passiert...

es bleibt ein großer batzen hängen - laut /proc/meminfo oder dem gebrauch von sysinfo... wir wissen, dass aufgrund des speichermanagements von linux die sache mit dem "wirklich" freien speicher keine so einfache milchmädchenrechnung ist, weil ungenutzte speicherbereiche noch einem prozess zugeordnet bleiben, solange kein anderer darauf angewiesen ist - deswegen wundert man sich auch oft, warum eine 08/15-anwendung unmengen an speicher benötigt... aber von freiem speicher alleine hat man schließlich nichts, daher ist der mechanismus an sich ja eigentlich recht gut, für uns wird er jedoch zum stolperstein... mit einer zweiten applikation, die bewusst speicher "frisst", damit der speicher freigegeben wird, hatten wir "teilerfolge"... leider ist das erstens keine besonders schöne lösung und zweitens bekommt man damit auch nicht 100% zurück :(

okay, an dieser stelle könnte man meinen, es wäre ein simples leak... wir verwenden ziemlich viel autopointer, was die leak-suche erschwert, weil die daten ja doch zerstört werden, und wenns erst im destruktor des letzten existierenden objekts passiert... hier haben wir versucht, speicherabbilder zur laufzeit zu bekommen, von verwaisten autopointern keine spur :(

nächster verdacht: speicherfragmentierung. bevor die ganze nachrichten als riesen-strings zusammengestückelt werden, gibts es etliche objekte, wiederum die pointer, enumtypen und sonstigen objekttechnischen kleinkram als container zusammenhalten... gut möglich, dass hier der speicher fragmentiert und eben einfach wo anders nach passenden speicherbereichen gesucht wird... wir hatten sogar einen teilerfolg, der die these belegen könnte...

mit der funktion 'mallopt' aus malloc.h haben wir probehalber M_MMAP_THRESHOLD auf 0 gesetzt, was dann zur folge hatte, dass der komplette speicher unserer anwendung via mmap() allokiert wurde, also rein durch memory pages und nicht am stack... die gute nachricht zuerst: der speicher kam mit fast 100% zurück, wenn "ausgekehrt" wurde... ABER: anscheinend sind unsere objekte so klein, dass einen x-fachen overhead pro allokation hatten, der speicherverbrauch stieg um ein vielfaches an... also nicht praktikabel... :(

unser letzter strohhalm war, eine methode (oder besser gesagt, methoden im sinne von C++) zu finden, die den speicherverbrauch etwas "realistischer" oder praxistauglicher zu berechnen... in der funktion 'mallinfo' von <malloc.h> gibt es einen wert 'fordblks', der angeblich den wert des "halbfreien" speichers in byte ausgab... dann hätten wir uns behelfen können und unseren freien speicher einfach aus <freemem>+<fordblks> berechnen könne, wäre zwar auch nur eine virtuelle größe gewesen, aber eben für unseren zweck realitätsnäher als /proc/meminfo und konsorten... dummerweise kamen wir hier auch nicht auf die erhofften werte... so errechnet war der wert des freien speichers zwar höher, aber immer noch weit weg von dem, was wir uns erwartet hätten...

an dieser stelle muss ich nun sagen, dass ich nicht der absolute linux-experte bin und mir aufgrund dieses problems einiges halbwissen angeeignet habe, welches ich nun hier in die waagschale werfe...

aber ich bin wirklich für jeden hint, link, gedankengang und jede idee dankbar, die hier eingeworfen wird...

gibt es vielleicht noch tools, mit denen man den code dahingehend untersuchen könnte (profiler, leakchecker... z.b. vailgrind, welches uns keine nennenswerten leaks bescheinigt hat)

wie kann man effektiv vermeiden, dass speicherfragmentierung auftritt? bis jetzt hab ich nur in erfahrung gebracht, dass es das beste wäre, eine eigene speicherverwaltung zu schreiben...

gibt es noch andere methoden, den tatsächlichen speicherverbrauch eines prozesses zu berechnen?

kann man das memorymanagement "tunen", ohne einen solchen overkill wie oben beschrieben zu beschwören?
ich erwarte von niemandem, wirklich die gesamte problematik zu erfassen, das tun wir wohl selber auch nicht... aber wenn euch irgendwas bekannt vorkommt und ihr lösungen/erfahrungswerte/tipps dazu habt, postet einfach mal... wir können doch nicht die einzigen sein, die auf nem kleinen gerät mit linux-memory-management probleme haben...

Bei Fragen zu versionen/tools usw. bitte keine falsche scheu, aber ich wollte es vorerst etwas grundsätzlicher halten, es muss ja nicht unbedingt an einer kombination bestimmter komponenten liegen...

viel dank für jeden input!

mfG DoNuT

locus vivendi
04-03-2008, 19:13
Dies ist noch keine richtige Antwort, vielleicht erbarmt sich ja jemand anderes der mit deinem Problem mehr Erfahrung hat.

Ich kann nur zum Besten geben es mal damit zu probieren Objekte in einem Pool zu allozieren, so dass nicht für jedes einzelne Objekt vom Betriebssystem (bzw. malloc/new) Speicher angefordert werden muss. Zu dieser Antwort komme ich deshalb, weil du ja schriebst das erst gepuffert wird, und dann in einem Rutsch alles bisherige Rausgehauen wird. Du musst nicht unbedingt die ganz Speicherverwaltung dafür selber neu schreiben, sonderen kannst vielleicht die Boost.Pool Bibliothek dafür nutzen (http://www.boost.org/libs/pool/doc/index.html). Ich würde auf jedenfall dazu raten, die Dokumentation zu lesen, vielleicht gibt es dort ja noch Anregungen für euch.

Wie groß sind die Objekte die euch Probleme bereiten denn eigentlich so, grob geantwortet? Im Bereich von einigen 100 Bytes? Kilobytes? Oder mehr oder weniger?

Eine vorerst letzte Idee von mir wäre noch die Anwendung in zwei Prozesse aufzuspalten, so dass ein Prozess die Rohdaten einliest (die ja wahrscheinlich kleiner sind als die XML-Nachrichten), und diese Rohdaten dann z.B. über eine Pipe an einen anderen Prozess weiterschickt, welcher die Daten in XML-Nachrichten umwandelt und übers Netzwerk versendet. Das macht es vielleicht einfacher die Speicherverwaltung zu optimieren, und würde es unter umständen auch ermöglichen den Netzwerk-Sender Prozess ab und zu neu zu starten um dem System eine Gelegenheit zu geben, Speicher aufzuräumen. Das ist jezt natürlich sehr spekulativ von mir, ich möchte ich keinesfalls auf eine falsche Fährte schicken.

DoNuT
04-03-2008, 19:37
tja, boost werden wir wohl nicht verwenden können... bei uns wird als C++-thirdparty-lib ACE verwendet, es ist kommerzielle software mit etwas zeitdruck dahinter, da muss alles etwas langsamere wege gehen... wir haben auf dem gerät wenig speicher, zusätzliche libs da reinzuziehen, wird wohl nicht gehen... mittlerweile haben wir auch eingesehen, dass ACE (noch dazu nicht in der aktuellesten version) nicht der weisheit letzter schluss ist, aber ein umsteig bietet sich im moment nicht an... dass wir rudimentär etwas equivalentes zu boost.pool schreiben, muss man sich ansehen, aber das ist schon mal eine möglichkeit...

das ganze in 2 eigenständige prozesse aufzusplitten, ist kaum drin... da steinigen uns archtekt, kunden, testteam und jeder, der davon wind kriegt... das programm sollte über einen zeitraum mehrerer tage kontinuierlich durchlaufen, und da wir eigentlich an einem 1.1.x-release arbeiten, bietet sich ein gründliches redesign nicht an... die anwendung ist sowieso schon "threaded as fuck", vom gerätetreiber bis zur klasse, die das zeug rausschickt, sind da schon deutlich mehr als 2 threads am direkten weg involviert, wäre wohl gefährlich, da was anzutasten ;)

kurz gesagt: uns sind die hände ziemlich gebunden, auf dem gerät läuft ein 2.4er-kernel, die libs dazu auch schon etwas angestaubt... und ob die toolchain 100%-ig sauber ist, können wir leider auch nicht verifizieren... deswegen hoffen wir, mit dem richtigen ansatz einen glückstreffer zu landen... probiert haben wir ja schon genug... z.b. statt 'vecstrVectorOfString.clear()' 'vecstrVectorOfString = vector< string >()', damit das ding neu angelegt wird, ein C++-garbagecollector lief leider nicht mangels aktueller libs, usw.... :(

kurz gesagt: wir können nichts essenzielles am code verändern, nur an den betreffenden stellen nachdrehen, bis jetzt haben wir immer gehofft, es wäre nur eine kleinigkeit, aber wir sind eben keine embedded-spezialisten und das teil ist leider unsere kleinste, aber auch wichtigste plattform für die software...

achja, die objektgröße der dinge, die dann später in eine nachricht konvertiert werden, ist wohl im bereich von max. ein paar 100 byte, nur eben ein ziemliches flickwerk, weil da so ziemlich alles an datentypen drinhängt, was die STL so hergibt, dazu noch die refcounted_auto_ptr aus ACE in massen... :(

*edit* ich sehe gerade, dass boost.pool nur includes sind, vielleicht lässt man das noch durchgehen, aber ich/wir sind für weitere ideen offen.... :D