malloc und lebensdauer



  • Ich entschuldige mich hier offiziell, falls ich irgendwelche Anmerkungen von Xin überlesen oder nicht richtig interpretiert haben sollte (oder einfach nochmals mit meinen Worten wiederholt haben sollte).

    Noch einige Anmerkungen:

    Xin schrieb:

    Das Löschen von Daten auf dem Stack wäre aber nicht performant und daher macht's vermutlich kein Compiler,...

    Dem ist so, so einen Code macht definitiv kein Compiler. Der Stack wird von Funktionsaufruf zu Funktionsaufruf beim Aufräumen nur dadurch bereinigt, indem der sogenannte Framepointer versetzt wird.

    Xin schrieb:

    Xin:
    Abgesehen davon dass es sehr unwahrscheinlich ist, dass diese durchgehend verwendeten Pages dieser Bereich ausgelagert würden, würden sie dann halt wieder eingeladen - und zwar inkl. der Daten, die die zuvor gerufene hinterlassen hat.

    Dem ist leider halt nicht so, nicht benötigte Memory-Pages werden von dem Prozess wieder freigegeben und ggf. anderen Prozessen zugeordnet. Die alten Daten werden nicht mehr eingelagert und sind somit nicht mehr verfügbar (soweit die Theorie vom Memory-Management; ist zwar - wie gesagt - eine unwahrscheinliche Situation, nämlich dass der Aufruf dieser externen Funktion Stackbereiche an der Grenze zu einer neuen Memory-Page belegt; tritt nach Murphy irgendwann auf, spätestens bei Last ;-))

    ... und wie gesagt, durch asynchrone Interrupts kann der User-Stack zu beliebigen Zeiten an der dann aktuellen Stelle (sprich Stackpointer) beginnend neu überschrieben werden. Auch in diesem Fall erhält man dann vermurkste Referenzen, die man als Listenpointer interpretiert oder mit denen man ein free() durchführen will. Ich schätze mal, dass das dem Prozess nicht gut bekommt.

    Die von Xin vorgeschlagene Methode mag vielleicht auch funktionieren, ich befürchte aber, dass sie (insbesondere bei hoher Systemlast) halt zu schwer reproduzierbaren Fehlern führen kann.



  • jox schrieb:

    Ich entschuldige mich hier offiziell, falls ich irgendwelche Anmerkungen von Xin überlesen oder nicht richtig interpretiert haben sollte (oder einfach nochmals mit meinen Worten wiederholt haben sollte).

    So offiziell ist das nicht notwendig, aber mich störte schon, dass sich das ganze wie eine Korrektur meiner gleichlautenden Aussagen las.
    Gegen eine Kritik an dieser Methode habe ich nichts einzuwenden, da es sich ja auch definitiv um einen zu kritisierenden Programmierstil handelt.

    jox schrieb:

    Xin schrieb:

    Xin:
    Abgesehen davon dass es sehr unwahrscheinlich ist, dass diese durchgehend verwendeten Pages dieser Bereich ausgelagert würden, würden sie dann halt wieder eingeladen - und zwar inkl. der Daten, die die zuvor gerufene hinterlassen hat.

    Dem ist leider halt nicht so, nicht benötigte Memory-Pages werden von dem Prozess wieder freigegeben und ggf. anderen Prozessen zugeordnet. Die alten Daten werden nicht mehr eingelagert und sind somit nicht mehr verfügbar (soweit die Theorie vom Memory-Management; ist zwar - wie gesagt - eine unwahrscheinliche Situation, nämlich dass der Aufruf dieser externen Funktion Stackbereiche an der Grenze zu einer neuen Memory-Page belegt; tritt nach Murphy irgendwann auf, spätestens bei Last ;-))

    Der Compiler gibt den Stack aber nicht frei, da der Stack ja weiterhin verwendet wird. Nehmen wir an, wir haben einen Stack von 8kB und Pages von 4kB, dann könnte sich das Szenario an der Grenze von 4kB im Stack bewegen. Da der Stack aber ein Block von 8kB Größe ist, der nicht freigegeben wurde, kann das Memory-Management nicht von sich aus entscheiden, dass es die eine Hälfte jetzt mal falsch wieder aus dem Swap-Space zieht. Dass es sich um einen Funktionsstack handelt wird bei malloc üblicherweise nicht mitangegeben und ob der Stack grade an der Stelle benutzt wird, die ausgelagert wird, wird das MemoryManagement wohl auch kaum prüfen.

    Schlussfolgerung: Der fragliche Segment wird ohne Veränderung wieder aus dem Swap-Space gezogen, niemals einfach nur freigegeben und die Tatsache, dass mein Zeiger eine Adresse ausgerechnet hat, zwischenzeitlich aber das Stack-Segment durch Swapping sich an eine andere Stelle des physikalischen Speichers verschoben, wird durch die MMU wieder grade gebogen.

    jox schrieb:

    Die von Xin vorgeschlagene Methode mag vielleicht auch funktionieren, ich befürchte aber, dass sie (insbesondere bei hoher Systemlast) halt zu schwer reproduzierbaren Fehlern führen kann.

    Alles Probleme, um die ich mich als Programmierer zur Ausführungszeit meines Programms nicht zu kümmern brauche, da ansonsten Swapping nicht funktionieren würde.

    Ich habe nie gesagt, dass die Methode Ausdruck von Stil und Eleganz beim Programmieren darstellen soll - im Gegenteil.
    Aber sie funktioniert, solange der Compiler beim Funktionsaufruf Argumente in gleicher Reihenfolge auf den Stack schreibt und die gesuchte Information als lokale Variable vorliegt.



  • (1)

    Xin schrieb:

    Der Compiler gibt den Stack aber nicht frei, da der Stack ja weiterhin verwendet wird. Nehmen wir an, wir haben einen Stack von 8kB und Pages von 4kB, dann könnte sich das Szenario an der Grenze von 4kB im Stack bewegen. Da der Stack aber ein Block von 8kB Größe ist, der nicht freigegeben wurde, kann das Memory-Management nicht von sich aus entscheiden, dass es die eine Hälfte jetzt mal falsch wieder aus dem Swap-Space zieht. Dass es sich um einen Funktionsstack handelt wird bei malloc üblicherweise nicht mitangegeben und ob der Stack grade an der Stelle benutzt wird, die ausgelagert wird, wird das MemoryManagement wohl auch kaum prüfen.

    Die Verwaltung des Stacks liegt nicht beim Compiler, sondern beim Betriebssystem. Die Größe des Stacks wird vom Betriebssystem bei Bedarf dynamisch angepasst (in beide Richtungen, d.h. in Deinem Beispiel könnte der Stack wieder auf 4kB reduziert werden).
    Was ich meine, ist nicht, dass beim Swappen ein Problem entsteht, sondern dass nicht mehr benötigte Memory-Pages dem Prozess evtl. auch nicht mehr zur Verfügung stehen. Ich muss daher meine ursprüngliche Aussage noch erweitern: Es stehen evtl. nicht nur andere Inhalte in einer freigegebenen Memory-Page, sondern darüber hinaus kann der ursprüngliche Prozess gar nicht mehr auf die Memory-Page zugreifen, weil sie ihm nicht mehr gehört (es gibt dann einen Memory-Zugriffsfehler).

    (2) ... und wie will man verhindern, dass durch asynchrone Interrupts der User-Stack wieder benutzt wird und die alten Daten dadurch überschrieben werden?

    Wie gesagt, die Methode mag zwar meistens funktionieren, sie ist aber nicht 100% sicher (dagegen sprechen sowohl (1) als auch (2)), und das gilt auch bei unveränderter Systemumgebung (Betriebssystem, Compiler) ... leider 😢



  • jox schrieb:

    Die Verwaltung des Stacks liegt nicht beim Compiler, sondern beim Betriebssystem. Die Größe des Stacks wird vom Betriebssystem bei Bedarf dynamisch angepasst (in beide Richtungen, d.h. in Deinem Beispiel könnte der Stack wieder auf 4kB reduziert werden).

    Der Punkt geht an Dich, konstante Stacks sind heutzutage nicht mehr State-of-the-Art.
    Auch wenn es quasi unmöglich ist, dass eine der Speicher so knapp verkleinert wird, dass die Daten der Funktion ebenfalls freigegeben werden. Trotzdem sehe ich die theoretische Möglichkeit, die Du ansprichst, und damit hast Du recht, dass meine Lösung nicht 100%ig sicher ist.

    jox schrieb:

    ... und wie will man verhindern, dass durch asynchrone Interrupts der User-Stack wieder benutzt wird und die alten Daten dadurch überschrieben werden?

    Grübel... hier muss ich erstmal fragen, ob die Interrupts nicht mit eigenem Stack vom OS vorbeischauen... schließlich landen wir hier in einer parallelen Verarbeitung, da reicht ein Stack nicht mehr.
    Da sie nämlich auch einschlagen können während der User-Stack grade in Bearbeitung ist, kann man den Stack nicht einfach mal so überschreiben.
    Aber das müsste ich nachschlagen und Du hast Deinen Beweis oben schon erbracht.



  • Xin schrieb:

    ... konstante Stacks sind heutzutage nicht mehr State-of-the-Art.

    Dynamische Anpassung der Stackgröße war bei Unix m.E. schon immer so (siehe auch das Standardwerk zum Uralt-Unix von Maurice Bach: Design of the UNIX Operating System von 1986)

    Xin schrieb:

    ... ob die Interrupts nicht mit eigenem Stack vom OS vorbeischauen...

    Bei den "normalen" Prozessoren (ungleich Mikroprozessoren) mit Multitasking-Unterstützung gibt es einen Kernel-Stack für Interrupts. Bei Unix gibt es aber auch die Möglichkeit, dass asynchron zum normalen Programmablauf Interrupts auf User-Ebene zuschlagen. Dies ist möglich über setjmp()/longjmp() oder auch über signals, wo dann jeweils der User-Kontext hergestellt wird und auch der User-Stack benutzt wird.

    Xin schrieb:

    Da sie (die Interrupts) nämlich auch einschlagen können während der User-Stack grade in Bearbeitung ist, kann man den Stack nicht einfach mal so überschreiben.

    Doch, das wird z.B. bei Mikroprozessoren (z.B. Z80 o.ä.) so gemacht: Auf einen Interrupt wird erst am Ende eines Befehls reagiert, d.h. laufende Stackoperationen (push, pop, call oder return) werden erst komplett ausgeführt, bevor der Interrupt abgearbeitet wird. Damit bleibt der Stack konsistent.

    ... für weitere Fragen (je HW-näher desto besser) bin ich auch gerne zu haben ...

    P.S.: Auf setjmp() hatte ich zwar bereits am 7.8. hingewiesen, aber so etwas kann man ja übersehen. Soll mir ja auch schom mal vorgekommen sein 😉



  • jox schrieb:

    Doch, das wird z.B. bei Mikroprozessoren (z.B. Z80 o.ä.) so gemacht: Auf einen Interrupt wird erst am Ende eines Befehls reagiert, d.h. laufende Stackoperationen (push, pop, call oder return) werden erst komplett ausgeführt, bevor der Interrupt abgearbeitet wird. Damit bleibt der Stack konsistent.

    die isr's benutzen auch selber den stack. sogar bei verschachtelten interrupts geht das...



  • jox schrieb:

    Dynamische Anpassung der Stackgröße war bei Unix m.E. schon immer so (siehe auch das Standardwerk zum Uralt-Unix von Maurice Bach: Design of the UNIX Operating System von 1986)

    Wir haben nicht alle das Glück unser ganzes Leben auf Unix-Derivaten verbringen zu dürfen. 8-(
    Abgesehen davon habe ich 1986 grade erst angefangen zu programmieren... PRINT "HALLO WELT"...
    Was Unix da für Funkionsstacks hatte oder ob Unix überhaupt existiert, war für mich da zweitrangig - da war ich noch stolz, dass ich mir '87 mein erstes Floppy-Laufwerk dazukaufen konnte... ^^

    jox schrieb:

    Xin schrieb:

    ... ob die Interrupts nicht mit eigenem Stack vom OS vorbeischauen...

    Bei den "normalen" Prozessoren (ungleich Mikroprozessoren) mit Multitasking-Unterstützung gibt es einen Kernel-Stack für Interrupts. Bei Unix gibt es aber auch die Möglichkeit, dass asynchron zum normalen Programmablauf Interrupts auf User-Ebene zuschlagen. Dies ist möglich über setjmp()/longjmp() oder auch über signals, wo dann jeweils der User-Kontext hergestellt wird und auch der User-Stack benutzt wird.

    Bzgl. setjmp und longjmpg() kann ich mich leicht rausreden, da die Frage iostreams enthielt, der Sourcecode damit im falschen Forum steht und man als C++ Programmierer derartige Probleme doch bitteschön via Exceptions zu lösen hat. ;-P

    jox schrieb:

    P.S.: Auf setjmp() hatte ich zwar bereits am 7.8. hingewiesen, aber so etwas kann man ja übersehen. Soll mir ja auch schom mal vorgekommen sein 😉

    *lach* Dann war das auch am 7.8. schon unpassend für ein C++-Programm. 😉



  • net schrieb:

    die isr's benutzen auch selber den stack. sogar bei verschachtelten interrupts geht das...

    ... genau das meinte ich. Mist, hatte ich meine Anmerkung schon wieder nicht exakt genug formuliert 😢



  • Xin schrieb:

    .. da die Frage iostreams enthielt, der Sourcecode damit im falschen Forum steht und man als C++ Programmierer derartige Probleme doch bitteschön via Exceptions zu lösen hat.

    iostreams oder andere Hinweise auf C++ habe ich zwar nicht entdecken können, aber auch für Exceptions gilt m.E. sehr wahrscheinlich, dass ein Teil der Laufzeitumgebung von C++ bei Signals sicherlich auch im User-Kontext läuft und den User-Stack benutzt, z.B. um bei Timer-Abläufen entsprechende Informationen für die Exceptions abzulegen.

    P.S.: Was für Floppy-Laufwerke meinst Du. Kennst Du noch 8-Zoll Floppys, einseitig, 250 kByte Kapazität? (Antwort ist nicht erforderlich...:-)



  • jox schrieb:

    Xin schrieb:

    .. da die Frage iostreams enthielt, der Sourcecode damit im falschen Forum steht und man als C++ Programmierer derartige Probleme doch bitteschön via Exceptions zu lösen hat.

    iostreams oder andere Hinweise auf C++ habe ich zwar nicht entdecken können,

    Das F-Wort... falscher Thread.... sry, dachte in dem Moment an ein Listing aus einem anderen Thread.

    jox schrieb:

    aber auch für Exceptions gilt m.E. sehr wahrscheinlich, dass ein Teil der Laufzeitumgebung von C++ bei Signals sicherlich auch im User-Kontext läuft und den User-Stack benutzt, z.B. um bei Timer-Abläufen entsprechende Informationen für die Exceptions abzulegen.

    Bei einer Exception ist bei solchen Tricks eh alles vorbei... weil man dann gar nicht weiß, wo die her kommt... 😉

    jox schrieb:

    P.S.: Was für Floppy-Laufwerke meinst Du. Kennst Du noch 8-Zoll Floppys, einseitig, 250 kByte Kapazität? (Antwort ist nicht erforderlich...:-)

    Nein, ich kaufte schon ein 5 1/4" Laufwerk, einseitig, 360kb. ^^
    Später auch ein 880kB 3,5" Laufwerk... das war damals, was heute eine Festplatte ist, unendlich groß und es war irrsinnig schnell. 😉


Anmelden zum Antworten