Autor Thema: SLAB-Allocator als Basis fuer kmalloc?  (Gelesen 37914 mal)

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #80 am: 31. May 2011, 11:11 »
Zitat von: erik
Der typische Programmierer wird sich aber nur wenig Gedanken über die Bedürfnisse des von ihm getippten Codes machen so dass das OS mit nur sehr wenigen Informationen eine möglichst gute Automatik füttern muss. Ich persönlich bin auch ehrlich der Meinung das es nicht unbedingt Aufgabe des Programmierers ist sich darum zu kümmern wie das System dafür sorgt das sein Programm erwartungsgemäß läuft.
Das mit dem Stack nimmt aber teilweise wirklich nicht mehr tragbare Ausmaße an. Wenn ich sehe das jemand versucht nen 16MB Array auf dem Stack zu allozieren (davon abgesehen, das der Compiler sowas gar nicht erst zulassen sollte) dann hört mein Verständnis dort auf.
Der Stack ist für Parameter und lokale (kleine) Variablen da und nicht dafür jeden Scheiß da drauf zu speichern (z.B. durch lokale Variablen in der Main-Funktion).
Wenn wir von einem Programm ausgehen, das nur einen Thread hat, sollte sich der Programmierer wirklich nicht darum kümmern müssen. Allerdings sollten ihm die OS Begrenzungen bekannt sein.
Geht es aber um ein Multithreading Programm sollte der Programmierer sich schon Gedanken machen wieviel Stack er wirklich braucht, alleine schon weil virtueller Speicher unter 32bit irgendwann knapp wird.

Zitat von: erik
Auf x86 wird man für TLS eher ein Register opfern und zwar ein Segment-Register (meist wohl FS oder GS, die selbst im Long-Mode noch gerade so genug vorhanden sind damit man damit TLS realisieren kann).
Genauso habe ich das bei mir umgesetzt. Problem ist jetzt noch die Art der Umsetzung, mache ich ein statisches Segment, wo also zum Ladezeitpunkt die Größe bekannt sein muss und man einfach nen neuen logischen Adressraum hat oder mache ich es so, dass das Programm auch zur Laufzeit die Größe ändern kann, was aber schwierig wird, zwecks virtueller Adressen.
Eine andere Variante (die ich irgendwo bevorzuge, aber die sich schlechter durch nen Compiler direkt umsetzen lässt) ist dass man jedem Thread (z.B.) 1024 Slots zur Verfügung stellt wo er Werte seiner Wahl drin speichern kann. Man würde dann z.B. sagen wenn man eine Freelist pro Thread hat, das man die Freelist im Slot 0 speichert und speicher die "0" in einer globalen Variable ab. Jeder Zugriff auf den Slot 0 würde dann den Thread lokalen Wert liefern.

Wesentlich schöner und einfacher für den Compiler/Programmierer ist natürlich ein abgetrennter Bereich wo das Segment dann hinzeigt. Da stellt sich mir dann aber die Frage wie man das umsetzt? Weil wo holt das OS den Speicher her um einen neuen Thread zu starten (er kann ja schlecht das malloc vom Programm nutzen) und was für Werte sollen da dann rein?

Zitat von: erik
Nein eigentlich nicht. Wenn Du das in SW machst dann ist diese Page komplett im Cache der aktuellen CPU und genau deswegen würde ich freie physische Pages auch CPU-lokal cachen.
Ich habe mich auch länger nicht mit den verschiedenen Caches der CPUs beschäftigt, aber gibt es nen separaten Cache zum Lesen und Schreiben? Weil ansonsten ist ja ne Cacheline (heutzutage) 64byte groß und wenn du dann eine ganze Page nullst, verdrängst du ja andere Cacheeinträge (und das meinte ich mit Cachetrashing).

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #81 am: 31. May 2011, 13:02 »
Hallo,


Wenn ich sehe das jemand versucht nen 16MB Array auf dem Stack zu allozieren (davon abgesehen, das der Compiler sowas gar nicht erst zulassen sollte) dann hört mein Verständnis dort auf.
Wo ist da Deiner Meinung nach das Problem? Wenn dieses Array nur innerhalb der Funktion benötigt wird dann ist der Stack IMHO genau der Ort wo es eben hingehört. Vor allem macht das die Funktion reentrant (im Gegensatz zu statischen Variablen) und schneller (kein Overhead für malloc/free).
Wo genau würdest Du die Grenze den ziehen? Und vor allem mit welcher Begründung? Etwa weil der VMM in Deinem OS nicht so gut mit flexiblen Stacks umgehen kann? Die Programmiersprache C macht den Stack nutzbar (bei z.B. Java ist das nicht so) und es ist meiner Meinung nach Aufgabe von Compiler und OS jedes valide C-Programm auch auszuführen, wenn Du das nicht möchtest dann benutze eben eine andere Programmiersprache.

Eine flexible TLS-Größe stelle ich mir in einem Flat-Memory-System auch schwierig vor, Du wirst auf jeden Fall die maximale Größe hart begrenzen müssen und immer gleich den gesamten virtuellen Adressraum allozieren (so wie beim Stack auch). Ich schätze mal den Hinweis das ich dieses Problem bei meinen Segmenten grundsätzlich nicht habe und mir sogar ne Art malloc/free für den TLS vorstellen kann kann ich mir doch jetzt sicher sparen, oder? ;)

Um für neue Threads neuen TLS-Speicher anzulegen kann man natürlich nicht das malloc vom Prozess nehmen sondern das ist Aufgabe des OS.

verdrängst du ja andere Cacheeinträge (und das meinte ich mit Cachetrashing).
Ah, jetzt verstehe ich. Ja das ist ein Problem wenn man das Ausnullen per SW macht, bei einer HW-Lösung ist das nicht so. Dem kann man aber entgegenwirken indem man passende Cache-Flush-Befehle oder Write-Through-Befehle benutzt, damit wird erreicht das diese Cache-Lines aus dem Cache raus fliegen und in den echten RAM zurückgeschrieben werden. Ich kann Dir da jetzt keine konkreten Befehle nennen aber die gehören wimre zur SSE2-Befehlserweiterung dazu, die Manuals Deines CPU-Herstellers sollten da entsprechend Auskunft geben können. Damit ist natürlich CPU-lokales cachen von freien physischen Pages nicht mehr so interessant, aber wimre wolltest Du das doch eh nicht machen.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #82 am: 31. May 2011, 13:22 »
Zitat von: erik
Wo ist da Deiner Meinung nach das Problem? Wenn dieses Array nur innerhalb der Funktion benötigt wird dann ist der Stack IMHO genau der Ort wo es eben hingehört. Vor allem macht das die Funktion reentrant (im Gegensatz zu statischen Variablen) und schneller (kein Overhead für malloc/free).
Sorry, aber wer 16MB auf den Stack packt der hat eindeutig was falsch gemacht (ist natürlich meine eigene Meinung).

Wieso nicht das Array vom Heap holen und den Pointer in einer lokalen Variablen Speichern? Um mit 16MB zu arbeiten, braucht man eh ein wenig. Dann kommt noch hinzu das gerade solch eine Größe auf dem Stack liegend wesentlich langsamer sein wird als wenn man malloc/free benutzt! Denn erstmal sind die nicht aligned, dann kommt hinzu das unter den meisten OS die Pages per Pagefault gemappt und alloziert werden und das es auch oft Probleme gibt, wenn man mehr als eine Page über das momentane Ende des Stacks hinausschießt.
Nutzt man malloc/free wird der Allocator bestimmt sofort die 16MB an das OS weiterleiten ohne in den eigenen Freelists zu gucken und bei der Größe könnte man sogar davon gebrauch machen mal die Portabilität außer acht zu lassen (wenn man Performance braucht) und das OS selber aufrufen, wo man dann auch sagen könnte das der Speicher sofort gemappt werden soll.

Ein weiteres Problem was ich daran sehe solche Stacks zuzulassen, stell dir mal ne rekursive Endlosschleife vor und da soll dann erst bei 16MB abgebrochen werden?

Zitat von: erik
Um für neue Threads neuen TLS-Speicher anzulegen kann man natürlich nicht das malloc vom Prozess nehmen sondern das ist Aufgabe des OS.
Und genau damit habe ich jetzt ein Problem. Ich gehe jetzt mal davon aus das der TLS 256byte pro Thread ist und da ich ja nicht noch ein eigenes malloc/free für den UserSpace implementieren möchte muss ich da jedes Mal eine ganze Page für nehmen. Ich kann mir irgendwie nicht vorstellen dass das so gelöst wurde (obwohl ich es "denen" durchaus zu traue ;) ).

Zitat von: erik
Dem kann man aber entgegenwirken indem man passende Cache-Flush-Befehle oder Write-Through-Befehle benutzt, damit wird erreicht das diese Cache-Lines aus dem Cache raus fliegen und in den echten RAM zurückgeschrieben werden. Ich kann Dir da jetzt keine konkreten Befehle nennen aber die gehören wimre zur SSE2-Befehlserweiterung dazu
Wo dann also z.B. die guten alten Athlon XPs rausfliegen würden oder einen totalen Performance Einbruch (?) haben würden. Von daher gefällt mir das schon nicht.

Das ist jetzt zwar wieder so eine Sache mit den Zielen die man erreichen möchte, aber ich denke mal die meisten OS Dever (gerade in Europa) vergessen dass nicht alle so moderne Rechner haben wie wir (sicher wer probiert schon so ein Hobby OS aus oder nutzt es) und gerade die Athlon XPs sind doch noch sehr verbreitet.
Bei meinem Vater auf Arbeit wird z.B. noch ein P3 mit Win2k eingesetzt und der ist für seine Aufgabe auch vollkommen ausreichend.
Auch ist das wieder so ein Thema der Herausforderung, wenn man natürlich von der perfekten Hardware ausgeht kann man alles leicht implementieren. Man muss sich halt nur die nötigen "guten" Rahmenbedingungen schaffen. Das ist ein wenig wie in der Schulphysik, findet alles unter optimalen Bedingungen und unter Vernachlässigung vieler Dinge statt ;)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #83 am: 31. May 2011, 14:03 »
Hallo,


Sorry, aber wer 16MB auf den Stack packt der hat eindeutig was falsch gemacht
Warum? Was?
Wenn mein Programm einen virtuellen Adressraum von 2 GB bekommt (und im PC genug RAM drin steckt) warum darf ich mich als Programmierer nicht dafür entscheiden diesen Speicher für den Stack zu benutzen? Ich empfinde es eben als doof wenn ich als Programmierer auf etwas Rücksicht nehmen muss (z.B. Flat-Memory) das eigentlich Compiler und OS vor mir verstecken sollten (laut C-Standard).
Speicher, der auf dem Stack am besten aufgehoben ist, vom Heap holen zu müssen kostet immer extra Performance. Der Stack ist eigentlich grundsätzlich schneller (wenn man im OS nichts falsch macht) als Heap. Auch auf dem Stack kann man (also der Compiler, wenn man das denn passend kommuniziert) Speicher passend ausrichten.

(ist natürlich meine eigene Meinung)
Die sei Dir auch absolut unbenommen, aber zumindest eine nachvollziehbare Begründung wäre schon nicht schlecht.

Ich freue mich jedenfalls schon darauf wenn VLA etwas mehr Verbreitung gefunden hat und dann viele erfrischend neue Probleme auftauchen. Falls ich bis dahin mit meinem Projekt was vorzeigbares auf die Füße bekommen hab kann ich dann immerhin sagen "mit der richtigen Speicherverwaltung wäre das nicht passiert". ;)

und das es auch oft Probleme gibt, wenn man mehr als eine Page über das momentane Ende des Stacks hinausschießt.
Also das wäre definitiv ein Bug im OS. Solange der Pagefault innerhalb des gültigen Stack des aktuellen Threads passiert muss das eigentlich auch ordentlich funktionieren.

Ich kann mir irgendwie nicht vorstellen dass das so gelöst wurde (obwohl ich es "denen" durchaus zu traue ;) ).
"denen" ist es völlig egal wie Du das in Deinem OS realisierst, solange das Ergebnis konform zum C-Standard ist. Natürlich haben "die" sich sicher auch mal überlegt wie das machbar ist, es ist wohl ziemlich unwahrscheinlich das "die" etwas standardisieren das sich auf üblichen Plattformen (Paging-basiertes Flat-Memory ist ja eben üblich) nicht ordentlich umsetzen lässt.

Das ist jetzt zwar wieder so eine Sache mit den Zielen die man erreichen möchte
Ganz recht. Wenn Du hohe Performance auch auf alten Systemen erreichen möchtest dann musst Du eben auf ein paar andere Features (wie z.B. das Ausnullen von Pages vor dem Recyceln) verzichten, wenn Du hingegen maximale Sicherheit auf aktuellen Systemen erreichen möchtest dann gehst Du da wieder anders ran. Eventuell kannst Du das ja hinter auswechselbaren Modulen verstecken.

Man muss sich halt nur die nötigen "guten" Rahmenbedingungen schaffen.
Darauf läuft es in der realen Praxis aber oft hinaus. Viele Probleme lassen sich mit vertretbaren Aufwand nicht korrekt lösen also muss man da ein paar Kompromisse eingehen und/oder Tricks anwenden. Du deklarierst doch auch eine maximale Stack-Größe um einen praktisch realisierbaren VMM zu erreichen.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #84 am: 31. May 2011, 14:23 »
Um nochmal auf das 16MB Array auf dem Stack zurückzukommen. Folgende Situation, der Adressraum des Programms ist voll und du kannst deinen Stack nicht mehr weiter vergrößern und da kommst du jetzt mit den Array welches auf dem Stack anglegt wurde (und ich gehe jetzt mal davon aus, das der Compiler nichts eingebaut hat um zu überprüfen ob der Stack noch genügend Platz bietet) und fängst an das Array zu füllen (natürlich beim Index 0). Jetzt bist du aber nicht nur eine Page (und zwar die Guard-Page) über das Stackende hinaus gegangen sondern sogar 16MB und das kann dann auch das beste OS nicht mehr abfangen (dafür bräuchte man dann wieder Segmente ;) ) und du greifst auf Speicher zu auf den du das besser nicht machen solltest.

Zitat von: erik
Ich freue mich jedenfalls schon darauf wenn VLA etwas mehr Verbreitung gefunden hat und dann viele erfrischend neue Probleme auftauchen.
Ich würde da einfach ab einer bestimmten Grenze sagen, das man das auf dem Heap speichert. Das kann der Compiler ja so implementieren, da eh eine Funktion aufgerufen werden muss um Speicher vom Stack zu bekommen.

Was mir gerade noch einfällt, bei mir in der Uni hat man uns auch schön gezeigt, das es keine gute Idee ist VLAs auf dem Stack zu allozieren, weil das Programm da zeitiger flöten geht als wenn man den Heap benutzt. Im konkreten Fall ging es darum ob man VLAs benutzt oder einfach new.

Zitat von: erik
Also das wäre definitiv ein Bug im OS. Solange der Pagefault innerhalb des gültigen Stack des aktuellen Threads passiert muss das eigentlich auch ordentlich funktionieren.
Siehe oben, das man da ab einer Gewissen Situation nichts mehr gegen machen kann.

Zitat von: erik
Du deklarierst doch auch eine maximale Stack-Größe um einen praktisch realisierbaren VMM zu erreichen.
Meinen VMM interessiert das mit dem Stack erstmal gar nicht (der weiß nicht mal das es sowas gibt), sondern ich erstelle einen ausreichend großen (was auch immer das ist) Bereich in dem die Pages durch Pagefaults gemappt werden (außer der ersten) und noch eine Guardpage.

Das machen übrigens Windows und Linux so, bei beiden hat der Stack eine genau definierte Größe. Unter Windows kann man das glaube ich sogar im EXE-Header einstellen. Ob er bei beiden zur Laufzeit vergrößet werden kann weiß ich nicht.

Der Punkt bleibt einfach, das man nicht davon ausgehen kann das ein Stack immer 16MB groß ist oder Platz für solche Objekte bietet.

Habe mich gerade mal in TLS im ELF eingelesen und es ist unter POSIX und Windows wirklich so, dass man sich nen Slot holt (der dann für alle Threads des Prozesses als used gilt) und dort einen Wert (natürlich nen Pointer) speichern kann. Was nun C/C++ betrifft, wird ein Slot immer pro Modul genutzt um alle TLS Variablen des Moduls dort abzuspeichern. Es wird also malloc/free des Programms genommen. Eigentlich auch klar da es eine Sprachbezogene Implementierung ist und keine Allgemeine.

Ich würde das dann wahrscheinlich so lösen, das jeder Thread genau eine Variable für sich speichern darf und dort kommt dann der Vektor für die Slots rein (weil das Problem ist, das der Vektor "unendlich" groß sein muss :( ). Ich hätte es zwar gerne anders gelöst, aber da wird das mit der Größe des Vektors zu umständlich.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #85 am: 31. May 2011, 16:08 »
Hallo,


Jetzt bist du aber nicht nur eine Page (und zwar die Guard-Page) über das Stackende hinaus gegangen sondern sogar 16MB und das kann dann auch das beste OS nicht mehr abfangen (dafür bräuchte man dann wieder Segmente ;) ) und du greifst auf Speicher zu auf den du das besser nicht machen solltest.
Genau diese Art Szenario hab ich auch als Begründung für Segmente in meinem aller ersten Thread hier im Forum benutzt. Genau das ist die Sorte Fehler die sich mit Flat-Memory grundsätzlich nicht lösen lässt und da wird VLA sicher noch viel Freude bringen. Ich vermute mal das die Compiler wieder damit anfangen werden vor jedem Allozieren auf dem Stack erst zu prüfen ob auf dem Stack noch ausreichend Platz ist, also Performance ist was anderes.

Ich würde da einfach ab einer bestimmten Grenze sagen, das man das auf dem Heap speichert.
Also zusätzlicher Code.

da eh eine Funktion aufgerufen werden muss um Speicher vom Stack zu bekommen.
Für was muss eine Funktion aufgerufen werden um Speicher auf dem Stack zu allozieren oder wieder frei zu geben? Das läuft auf eine simple Addition/Subtraktion mit dem Stack-Pointer hinaus, also einem einzelnen Assembler-Befehl. Erst das Prüfen auf ausreichend Platz macht üblicherweise eine kleine Funktion, ich hab so eine mal für den Watcom-Compiler implementiert.

Was mir gerade noch einfällt, bei mir in der Uni hat man uns auch schön gezeigt, das es keine gute Idee ist VLAs auf dem Stack zu allozieren, weil das Programm da zeitiger flöten geht als wenn man den Heap benutzt. Im konkreten Fall ging es darum ob man VLAs benutzt oder einfach new.
Hast Du da nen Script o.ä.? Es würde mich mal sehr interessieren mit was für Argumenten und Szenarien da gearbeitet wurde.

Der Punkt bleibt einfach, das man nicht davon ausgehen kann das ein Stack immer 16MB groß ist oder Platz für solche Objekte bietet.
Ja leider, und das trotz dessen das in einem heutigen PCs locker mal ein paar GB Speicher stecken. Ich empfinde das als beschämend.

und es ist unter POSIX und Windows wirklich so, dass man sich nen Slot holt (der dann für alle Threads des Prozesses als used gilt) und dort einen Wert (natürlich nen Pointer) speichern kann....
Das erscheint mir irgendwie kompliziert und langsam, ein Haufen indirektes Zeug.
Ich möchte einfach alle TLS-Variablen in einer extra Sektion sammeln und der Linker macht dann daraus ein spezielles Segment das dann vom OS (zur Laufzeit) für jeden Thread der auf seinen TLS schreibend zugreift einmal geklont wird (COW). Damit kann ich TLS sogar aus simplen Assembler-Programmen benutzen ohne irgendwelche Verrenkungen und zusätzliche Befehle, ganz so wie alle anderen statischen Variablen auch. Ich dachte eigentlich dass das auf x86 ähnlich gemacht wird, schließlich hat man dort ja Segmente.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #86 am: 31. May 2011, 16:29 »
Eine flexible TLS-Größe stelle ich mir in einem Flat-Memory-System auch schwierig vor, Du wirst auf jeden Fall die maximale Größe hart begrenzen müssen und immer gleich den gesamten virtuellen Adressraum allozieren (so wie beim Stack auch). Ich schätze mal den Hinweis das ich dieses Problem bei meinen Segmenten grundsätzlich nicht habe und mir sogar ne Art malloc/free für den TLS vorstellen kann kann ich mir doch jetzt sicher sparen, oder? ;)
Dafür braucht man aber keine Segmente. Ein einziger Pointer als TLS reicht, um alles machen zu können, wenn man nicht die Threads voneinander abschotten will. Letztendlich ist ja in der Regel %fs auf x86_64 genau dieser eine Pointer und nicht mehr. Und in der Struktur, auf die er zeigt, kann man dann eine Freispeicherliste für das threadlokale malloc aufhängen, wenn man mag.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #87 am: 31. May 2011, 16:33 »
Zitat von: erik
Für was muss eine Funktion aufgerufen werden um Speicher auf dem Stack zu allozieren oder wieder frei zu geben? Das läuft auf eine simple Addition/Subtraktion mit dem Stack-Pointer hinaus, also einem einzelnen Assembler-Befehl. Erst das Prüfen auf ausreichend Platz macht üblicherweise eine kleine Funktion, ich hab so eine mal für den Watcom-Compiler implementiert.
Genau für das Prüfen ob genügend Platz ist. Dafür wird bei VLAs eine Funktion aufgerufen und da kann ich dann auch gleich malloc aufrufen (muss dann ntürlich am Ende der Funktion auch noch free aufrufen). Der Unterschied sollte, je nach Größe mal auf dem Stack und mal auf dem Heap schneller sein. Deswegen sollte sowas auch der Programmierer entscheiden, der weiß am besten wie groß die Arrays am meisten sind.

Zitat von: erik
Hast Du da nen Script o.ä.? Es würde mich mal sehr interessieren mit was für Argumenten und Szenarien da gearbeitet wurde.
Nope, das war innerhalb einer Übung, eigentlich wollte der Prof da gar nicht drauf eingehen (insbesondere da er nicht dachte das jemand versucht ein solches VLA auf dem Stack anzulegen, aber Studenten haben noch jede Dummheit mitgenommen ;) ), aber hat dann in der Übung schön zeigen können, das ab einer gewissen Arraygröße auf dem Stack das Programm beendet wird (zwecks nicht genug Stack) und wenn man new (also den Heap) nutzt funktioniert das bis zu wesentlich größeren Größen.

Zitat von: erik
Ja leider, und das trotz dessen das in einem heutigen PCs locker mal ein paar GB Speicher stecken. Ich empfinde das als beschämend.
Nicht wirklich (unter 64bit ja). Denn denk an ALR (oder wie das doch gleich hieß). Irgendwo musst du da bei der Stackgröße ne Grenze ziehen und wenn dann noch mehrere Threads dazu kommen eh!

Zitat von: erik
Das erscheint mir irgendwie kompliziert und langsam, ein Haufen indirektes Zeug.
Ich möchte einfach alle TLS-Variablen in einer extra Sektion sammeln und der Linker macht dann daraus ein spezielles Segment das dann vom OS (zur Laufzeit) für jeden Thread der auf seinen TLS schreibend zugreift einmal geklont wird (COW). Damit kann ich TLS sogar aus simplen Assembler-Programmen benutzen ohne irgendwelche Verrenkungen und zusätzliche Befehle, ganz so wie alle anderen statischen Variablen auch. Ich dachte eigentlich dass das auf x86 ähnlich gemacht wird, schließlich hat man dort ja Segmente.
Genauso hat man es dann auch gemacht in der ELF und C/C++ Implementierung. Da macht man sich das gs Register zu nutze und das beide Systeme es zulassen einen Wert zu speichern und den über "gs:0" anzusprechen. Es gibt da allerdings noch ein paar Sachen die mir nicht ganz einleuchten in dem "Werk", aber das kann ich dann lösen wenn ich das mal portiere.

Im Endeffekt ist es allgemein (weil es ja Platformübergreifend sein soll) so, das man ne Funktion aufruft (sowas wie tls_get_data(size_t mid, size_t offset)) und die Daten bekommt (oder die Adresse, bin mir da gerade nicht sicher), aber auf x86 wird das alles vereinfacht und kein call zu dieser Funktion gemacht, sondern mit dem gs Register gearbeitet.

Kannst du dir ja bei Gelegenheit mal angucken.

Zitat von: taljeth
Dafür braucht man aber keine Segmente.
Und was ist deiner Meinung nach das fs Register ;)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #88 am: 31. May 2011, 17:18 »
Der Treiber kann den gesamten Speicher auslesen, weil er halt Zugriff auf die Hardware hat und diese anweisen kann per DMA den gesamten Speicher auslesen. Das war das was ich meinte!
Und das ist so nicht richtig: Nicht jede Hardware kann DMA und nicht jede Hardware kann die gerade per DMA gelesenen Daten auch wieder zurückschieben. Auch kann nicht jede Hardware so einen Durchsatz garantieren, den du für unbemerktes Memdumping möchtest, zumal währenddessen die Hardware wahrscheinlich ausgelastet ist. Außerdem ist der Treiber dann garantiert hardwarespezifisch - und ein gutes OS lässt nicht zwei Treiber auf die gleiche Hardware zugreifen, d.h. man müsste den schon vorhandenen Treiber manipulieren, statt einfach einen beliebigen Treiber zu injizieren.

Ein paar Treiber werden in der Lage sein, den Speicher halbwegs schnell mit Hilfe der Hardware zu dumpen, aber davor kannst du dich ohne IOMMU nicht schützen. Dennoch sollte ein Treiber nicht auf den gesamten Speicher zugreifen können; versucht er es dennoch, gibt es halt einen GPF.

Performance ist was anderes
Auch in einer IOMMU kann man einen TLB implementieren. [...]
Das bezog sich darauf, dass man, wenn man mit einer Hardware den Speicher ohne IOMMU auslesen möchte, die Daten halt zweimal mit DMA (oder DMA+PIO) durch langsame Subsysteme transportieren muss. Das ist nicht besonders performant.

Gruß,
Svenska

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #89 am: 31. May 2011, 17:24 »
Zitat von: svenska
Und das ist so nicht richtig: Nicht jede Hardware kann DMA und nicht jede Hardware kann die gerade per DMA gelesenen Daten auch wieder zurückschieben.
Es bleibt ja allgemein erstmal gültig, das es im speziellen dann Geräte gibt die etwas nicht können, änder ja nichts an der Tatsache das es Treiber gibt die es können.
Die Geschwindigkeit spielt dann meine Meinung nach keine Rolle mehr, es muss ja nicht der gesamte Speicher ausgelesen werden, es reicht ja auch der nötige ;)

Zitat von: svenska
Dennoch sollte ein Treiber nicht auf den gesamten Speicher zugreifen können; versucht er es dennoch, gibt es halt einen GPF.
Was unter einem Mikrokernel klar sein sollte, aber in nem monolithen kannst du das nicht hinbekommen (nicht auf jeder Architektur und nicht ohne sehr sehr viel Aufwand).

Zitat von: svenska
Das bezog sich darauf, dass man, wenn man mit einer Hardware den Speicher ohne IOMMU auslesen möchte, die Daten halt zweimal mit DMA (oder DMA+PIO) durch langsame Subsysteme transportieren muss.
Nicht unbedingt, wenn du den ausgelesenen Speicher einfach per Netzwerk rausschickst.

@taljeth

Du hast recht, man braucht dafür nicht unbedingt Segmente, aber dann ein freies Register, wo man nen Thread spezifischen Wert speichern kann. Was aber unter x86 (auch nicht im 64bit Modus) ratsam ist, da zu wenige Register bzw. auf bestimmte Register nicht aus dem UserMode zugegriffen werden kann (z.B. Debugging-Register die man ja für sowas missbrauchen könnte).

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #90 am: 31. May 2011, 19:58 »
Zitat von: taljeth
Dafür braucht man aber keine Segmente.
Und was ist deiner Meinung nach das fs Register ;)
Eine Segmentruine. ;)

Letztendlich ist es auf x86_64 nicht wesentlich mehr als ein Register mit einem Pointer, den man für manche Adressierungsarten draufaddieren kann. Das ist eine relative bequeme Methode, aber dafür kann man genausogut andere Mechanismen hernehmen, z.B. ein Allzweckregister reservieren oder was Nicht-x86-Architekturen eben sonst machen, um TLS zu implementieren. Du meinst doch nicht, dass es TLS nur auf x86 gibt, oder?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #91 am: 31. May 2011, 20:01 »
Das bezog sich darauf, dass man, wenn man mit einer Hardware den Speicher ohne IOMMU auslesen möchte, die Daten halt zweimal mit DMA (oder DMA+PIO) durch langsame Subsysteme transportieren muss. Das ist nicht besonders performant.
Ich habe es nicht konkret ausprobiert, aber der Datentransfer, den ich brauche, um Ring 0 zu kriegen, dürfte sich sehr in Grenzen halten. Und alles weitere kann ich dann ja wieder direkt im RAM machen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #92 am: 31. May 2011, 20:18 »
Zitat von: taljeth
Letztendlich ist es auf x86_64 nicht wesentlich mehr als ein Register mit einem Pointer, den man für manche Adressierungsarten draufaddieren kann.
Auf der einen Seite ist es eigentlich egal, aber es werden dort keine Limit-Checks mehr durchgeführt?

Zitat von: taljeth
Du meinst doch nicht, dass es TLS nur auf x86 gibt, oder?
Die nehmen ein Allgemeinregister, aber das ist unter x86 nicht zu empfehlen. Gibt es eigentlich noch eine Architektur (die man Leistungsmäßig zumindestens mit nem ARM Cortex A8 vergleichen kann) die auch nur so wenig Register hat und keine Segment Register?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #93 am: 31. May 2011, 21:36 »
Hallo,


Dafür braucht man aber keine Segmente. Ein einziger Pointer als TLS reicht, um alles machen zu können, wenn man nicht die Threads voneinander abschotten will. Letztendlich ist ja in der Regel %fs auf x86_64 genau dieser eine Pointer und nicht mehr. Und in der Struktur, auf die er zeigt, kann man dann eine Freispeicherliste für das threadlokale malloc aufhängen, wenn man mag.
Ja, es geht auch ohne Segmente, aber die volle Flexibilität und Performance die man mit Segmenten ohne irgendwelche Zusatzticks bieten kann wird man mit Flat-Memory trotzdem nicht hinbekommen (ob man diese Flexibilität auch tatsächlich benötigt ist jetzt mal unerheblich). Gerade dieser Mechanismus mit den Slots (den FlashBurn beschrieben hat) dürfte in irgendeiner Form zur Compilezeit begrenzt sein ansonsten wird das Modifizieren der Strukturen nur noch wesentlich komplizierter.


Genau für das Prüfen ob genügend Platz ist. Dafür wird bei VLAs eine Funktion aufgerufen
Naja, kann man so machen, aber ob das die effizienteste Lösung ist möchte ich dennoch bezweifeln.

und da kann ich dann auch gleich malloc aufrufen (muss dann ntürlich am Ende der Funktion auch noch free aufrufen). Der Unterschied sollte, je nach Größe mal auf dem Stack und mal auf dem Heap schneller sein.
Also ich behaupte das Speicher allozieren auf dem Stack immer schneller ist, egal ob es sich um 4 Bytes oder um 1GByte handelt, es bleibt ne simple Addition/Subtraktion. Ich sehe auch nicht wie das überhaupt von der Größe beeinflusst werden könnte.

Deswegen sollte sowas auch der Programmierer entscheiden, der weiß am besten wie groß die Arrays am meisten sind.
Gerade der Programmierer weiß oft am wenigsten wie groß der Workload später tatsächlich wird. Der kann höchstens ein paar Abschätzungen und Worst-Case-Betrachtungen durchführen oder eine optimale Workload-Größe für den Anwender empfehlen.

(insbesondere da er nicht dachte das jemand versucht ein solches VLA auf dem Stack anzulegen, aber Studenten haben noch jede Dummheit mitgenommen ;) )
Wieso nur betrachtest Du das allozieren von Speicher auf dem Stack als Dummheit? Ich vermisse da immer noch ein handfestes Argument.

das ab einer gewissen Arraygröße auf dem Stack das Programm beendet wird (zwecks nicht genug Stack)
Achso, es geht darum ob es überhaupt läuft. Du hattest heute Mittag von "da zeitiger flöten geht" geschrieben und da hatte ich vermutet das Du irgendein Laufzeitproblem o.ä. meinst.

Zitat von: erik
Ja leider, und das trotz dessen das in einem heutigen PCs locker mal ein paar GB Speicher stecken. Ich empfinde das als beschämend.
Nicht wirklich .....
Doch, das ist beschämend. Völlig egal wie viel RAM wirklich im Rechner steckt, das es auf einer >=32Bit-Plattform nicht möglich ist einfach mal 16 MByte oder auch 256 MByte auf dem Stack abzulegen ist einfach nur beschämend. Ich verstehe natürlich die technischen Zwänge hinter Flat-Memory die sowas eben unmöglich machen aber von außen betrachtet, ohne sich um die tatsächliche Realisierung der Speicherverwaltung zu kümmern, ist es einfach unverständlich warum ich als Programmierer den vorhandenen Speicher nicht so benutzen kann wie ich das gerne möchte.

Im Endeffekt ist es allgemein (weil es ja Platformübergreifend sein soll) so, das man ne Funktion aufruft (sowas wie tls_get_data(size_t mid, size_t offset))
Ich denke man wird eher ein Register opfern. Wenn 16 oder mehr Register vorhanden sind dürfte das eher verschmerzbar sein als ein teurer Funktionsaufruf (vor allem kann man dieses Register ja auch mal auf dem Stack sichern wenn im entsprechenden Code-Abschnitt kein TLS benutzt wird und gerade dieses eine Register fehlt). Das x86 dafür Segment-Register hat ist da eher ein nettes Zusatzfeature so das man wenigstens keines der normalen Register opfern muss.


Das bezog sich darauf, dass man, wenn man mit einer Hardware den Speicher ohne IOMMU auslesen möchte, die Daten halt zweimal mit DMA (oder DMA+PIO) durch langsame Subsysteme transportieren muss. Das ist nicht besonders performant.
Wenn man den kompletten RAM dumpen will wird das so oder so nicht besonders performant. Wohin möchtest Du denn den vorhandenen Speicher kopieren? Etwa in den vorhandenen Speicher? Nein, man wird eh den Speicherinhalt komplett irgendeiner Hardware übergeben müssen, und ob das nun der HDD-Controller oder der Ethernet-Controller ist spielt da IMHO keine so große Rolle mehr. Wenn es aber darum geht eigenen Code in den Kernel zu injizieren bzw. auf anderem Weg Ring-0-Rechte zu erlangen dann dürfte die erforderliche Datenmenge ziemlich klein sein, wenn man die Adressen genau kennt dürfte das mit weniger als ein paar KByte auskommen und das geht dann wirklich schnell genug.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #94 am: 31. May 2011, 21:38 »
Völlig egal wie viel RAM wirklich im Rechner steckt, das es auf einer >=32Bit-Plattform nicht möglich ist einfach mal 16 MByte oder auch 256 MByte auf dem Stack abzulegen ist einfach nur beschämend. Ich verstehe natürlich die technischen Zwänge hinter Flat-Memory die sowas eben unmöglich machen aber von außen betrachtet, ohne sich um die tatsächliche Realisierung der Speicherverwaltung zu kümmern, ist es einfach unverständlich warum ich als Programmierer den vorhandenen Speicher nicht so benutzen kann wie ich das gerne möchte.
Man kann doch den Stack-Pointer verändern?!?
Dieser Text wird unter jedem Beitrag angezeigt.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #95 am: 31. May 2011, 21:55 »
Man kann doch den Stack-Pointer verändern?!?
ist anzunehmen, ja. Und was willst Du damit sagen?!?
Reality is that which, when you stop believing in it, doesn't go away.

FlashBurn

  • Beiträge: 844
    • Profil anzeigen
Gespeichert
« Antwort #96 am: 31. May 2011, 21:55 »
Zitat von: erik
Naja, kann man so machen, aber ob das die effizienteste Lösung ist möchte ich dennoch bezweifeln.
So wie ich das verstanden hatte, ist das keine Sache von kann man machen, sondern muss man machen, es muss jede Page angefasst und zwar in der richtigen Reihenfolge, damit die Guardpage eine Page weiter gesetzt wird.
Das Problem ist hier die Sicherheit, das man bestimmte Sachen verhindern möchte. Dazu kommt halt noch ALR, wo es dann sein kann das du zwar viel freien RAM hättest der Stack aber an einer Stelle ist wo er einfach nicht größer werden kann. Vorallem was willst du machen wenn der Speicher voll ist, dann bekommst du das da auch nicht drauf.
Dann kommt noch hinzu wie der VMM aufgebaut ist, bei mir ist es z.B. nicht einfach mal möglich sich neuen Speicher zu holen in dem man einfach willkürlich auf eine Page zugreift und die dann gemappt wird, sondern der Speicher muss vom OS angefordert werden.

Zitat von: erik
Wieso nur betrachtest Du das allozieren von Speicher auf dem Stack als Dummheit? Ich vermisse da immer noch ein handfestes Argument.
Also erstmal sollte man wissen das der Stack begrenzt ist und wo diese Grenze ungefähr liegt und da sollte es klar sein, das ab einer Gewissen Größe kein Platz mehr auf dem Stack ist.
Dazu kommt, das der Stack für mich für kleine überschaubare Sachen zuständig ist, aber ab einer gewissen Größe ist es nicht mehr nur kurzzeitig sondern länger und da fällt es dann auch nicht mehr ins Gewicht ob man malloc oder den Stack benutzt.

Zitat von: erik
Achso, es geht darum ob es überhaupt läuft. Du hattest heute Mittag von "da zeitiger flöten geht" geschrieben und da hatte ich vermutet das Du irgendein Laufzeitproblem o.ä. meinst.
Ich weiß die Aufgabe nicht mehr genau, aber einer Funktion wurde die Größe übergeben und daraufhin hat man nen neues Array von Int Werten angelegt, was man dann damit gemacht hat, keine Ahnung mehr. Und wenn du das auf dem Stack machst, geht es relativ zeitig flöten, da du halt versuchst auf Speicher zuzugreifen, auf den du keine Rechte hast und machst du das über/malloc geht das wesentlich länger (größer).

Zitat von: erik
Doch, das ist beschämend. Völlig egal wie viel RAM wirklich im Rechner steckt, das es auf einer >=32Bit-Plattform nicht möglich ist einfach mal 16 MByte oder auch 256 MByte auf dem Stack abzulegen ist einfach nur beschämend.
Wie gesagt, was machst du wenn du soviel Platz auf dem Stack nicht findest, aber sich woanders im Prozess eine Lücke findet die groß genug ist? Was machst du bei mehreren Threads die alle nen Stack brauchen und da willst du dann jedes Mal 256MB für den Stack (eigentlich ja sogar mehr) reservieren? Und wo packst du Libraries und den Heap hin?

Zitat von: erik
Ich denke man wird eher ein Register opfern.
Ist ja richtig, aber auf x86 wird zum Bsp gs und auf x86-64 fs genommen, weil halt kein Register über ist und da man den Standard Platformunabhängig machen wollte hat man sich für die Funktionen entschieden. Das jede Platform diese Funktionen anders implementiert und diese dann vom Compiler geinlinet werden, das ist klar und dann läuft es genau auf solch ein Register hinaus.

Zitat von: PorkChicken
Man kann doch den Stack-Pointer verändern?!?
Das werde ich z.B. unter meinem OS nicht zulassen. Ich überprüfe jedes Mal wenn ein Syscall gemacht wird, ob der Stackpointer noch innerhalb des Stackbereichs liegt und wenn nicht dann gibts entweder nen Fehlercode oder der Prozess wird beendet.
Das der Programmierer sich selbst um den Stack gekümmert hat, die Zeiten sind vorbei, vorallem umgeht er damit ja alle Sicherheits Maßnahmen vom OS (und darauf legt ihr ja immer wert ;) ).

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #97 am: 31. May 2011, 21:59 »
@Erik: Dass es sich nicht lohnt soviele Worte darüber zu verlieren, dass man angeblich nicht den vorhanden Speicher so nutzen kann wie man möchte.
Dieser Text wird unter jedem Beitrag angezeigt.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #98 am: 31. May 2011, 22:33 »
Das werde ich z.B. unter meinem OS nicht zulassen. Ich überprüfe jedes Mal wenn ein Syscall gemacht wird, ob der Stackpointer noch innerhalb des Stackbereichs liegt und wenn nicht dann gibts entweder nen Fehlercode oder der Prozess wird beendet.
Das der Programmierer sich selbst um den Stack gekümmert hat, die Zeiten sind vorbei, vorallem umgeht er damit ja alle Sicherheits Maßnahmen vom OS (und darauf legt ihr ja immer wert ;) ).
Welche Sicherheitsmaßnahmen umgeht man denn mit einem Stackwechsel? Und wenn das geht, heißt das nicht, dass diese Sicherheitsmaßnahmen schlicht und einfach nichts taugen?
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #99 am: 01. June 2011, 10:04 »
Hallo,


es muss jede Page angefasst und zwar in der richtigen Reihenfolge
Warum muss jede Page vom Stack in der richtigen Reihenfolge angefasst werden?
Die Guard-Page ist IMHO immer die letzte Page (bei x86 die an der niedrigsten Adresse) vom maximal möglichen Stack und die wird prinzipiell nie gemappt und ein Zugriff führt immer zum Stack-Overflow-Kill.

Vorallem was willst du machen wenn der Speicher voll ist, dann bekommst du das da auch nicht drauf.
Wenn der Speicher voll ist ist eh zu spät aber wenn mein Programm in seinem virtuellen Adressraum noch knapp 2 GBytes frei hat dann sollte es eigentlich auch möglich sein mal 16 MBytes auf den Stack zu legen.

Dann kommt noch hinzu wie der VMM aufgebaut ist
Der C-Standard interessiert sich nicht für den VMM, dort wird mir gesagt das ich alles mögliche als lokale Variable auf den Stack legen kann und fertig. Wie das dann konkret umgesetzt wird ist ein Implementierungsdetail. Meiner persönlichen Meinung nach ist es Aufgabe der Implementierung (was Compiler, libc, OS usw. mit einschließt) die Dinge die der C-Standard zusichert auch zur Verfügung zu stellen und gerade an der Stelle hat Flat-Memory eben eine Schwäche mit dem Stack. Mir ist klar warum diese Schwäche existiert und ich muss diese Schwäche auch in meinen Programmen berücksichtigen aber ich empfinde das eben als unpraktisch und nicht nachvollziehbar.

und wo diese Grenze ungefähr liegt
Warum sollte ich das als reiner C-Programmierer wissen? Mich interessieren doch nicht die spezifischen Eigenheiten der drunter liegenden Plattform, deswegen gibt es doch den Compiler (und die restliche Kette bis hin zum OS) damit diese Dinge vor mir verborgen werden und ich nur den C-Standard kennen muss.

Dazu kommt, das der Stack für mich für kleine überschaubare Sachen zuständig ist
Definiere "kleine". Wenn Du da eine Grenze ziehst dann musst Du die auch sauber begründen können. Der C-Standard zieht dort keine Grenze. Natürlich ist dem Programmierer bewusst das Speicher nicht unbegrenzt zur Verfügung steht aber warum soll der Stack eine "kleine" Grenze haben und der Heap nicht?

Wie gesagt, was machst du wenn du soviel Platz auf dem Stack nicht findest, aber sich woanders im Prozess eine Lücke findet die groß genug ist? Was machst du bei mehreren Threads die alle nen Stack brauchen und da willst du dann jedes Mal 256MB für den Stack (eigentlich ja sogar mehr) reservieren? Und wo packst du Libraries und den Heap hin?
Diese Dinge fallen allesamt in den Verantwortungsbereich der Implementierung und nicht des C-Programmierers.


Dass es sich nicht lohnt soviele Worte darüber zu verlieren, dass man angeblich nicht den vorhanden Speicher so nutzen kann wie man möchte.
Wenn Du das mit dem "angeblich" ernst meinst dann beweise mir das man mit reinen ANSI-C-Mitteln 256 MBytes auf den Stack legen und benutzen kann! Auf Windows und Linux (und noch mindestens einer nicht-x86-32Bit-Plattform mit normalem/unmodifiziertem General-Purpose-OS), ohne zusätzliche (OS-spezifische) Librarys und ohne Compiler/Assembler/Linker/....-spezifische Tricks.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

 

Einloggen