pic vs. segmentierung



  • ich weiß, dass das ziemlich off topic ist, aber ich denke, hier ist das noch am wenigsten ot.

    ich hab mir gedacht, man erzeuge für jede shared lib ein codesegment. man könnte 1024 codesegmentdescriptoren in der gdt dafür reservieren (1024 libs gleichzeitig geladen zu haben ist denk ich nicht zu wenig; vor allem, weil ja die meisten programme die gleichen libs verwenden.) dadurch wäre pic nicht mehr nötig, da alle libs immer bei offset null beginnen würden und so glauben, sie wären aller an der gleichen stelle.

    laut intel dokumentation braucht ein far call nur den speicherzugriff auf die gdt/ldt, um an den descriptor zu kommen, mehr als ein near call, da bei JEDEM versuch etwas auszuführen die rechte des cs geprüft werden. wozu wäre sonst das ganze segmentregister mit den puffern für die rechte gut? wenn eine rechteprüfung nur bei änderung des cs stattfinden würde, dann bräuchte es keine pufferung. außerdem kommt die gp exception ja nicht beim ändern des cs, sondern beim ausführen des nächsten befehls.

    pic braucht aber auch einen speicherzugriff: den auf die got.

    dh, dass beide methoden sicher langsamer sind, als ein direkter near call, aber das ändern des cs mit großer sicherheit performanter ist, als der zugriff auf die got. außerdem würde dann das register ebx nicht permanent ausfallen. aufrufe von einer funkton der shared lib innerhalb der shared lib wäre dann wieder ein normaler near call. ob, bei pic die adressen von lib internen funktionen berechnet werden müssen, wenn sie von lib funktionen aufgerufen werden, weiß ich aber nicht...

    welche gründe kann es jetz noch geben, doch pic zu erzeugen? bitte um anregungen und meinungen 🙂 ich hoffe, ich hab nicht vollkommenen blödsinn geschrieben.

    björn



  • Original erstellt von <mastamind>:
    **laut intel dokumentation braucht ein far call nur den speicherzugriff auf die gdt/ldt, um an den descriptor zu kommen, mehr als ein near call, da bei JEDEM versuch etwas auszuführen die rechte des cs geprüft werden. wozu wäre sonst das ganze segmentregister mit den puffern für die rechte gut? wenn eine rechteprüfung nur bei änderung des cs stattfinden würde, dann bräuchte es keine pufferung. außerdem kommt die gp exception ja nicht beim ändern des cs, sondern beim ausführen des nächsten befehls.
    **

    Ja moment mal. Die rechtzeüberprüfung findet bei einem NEAR call NICHT statt, wozu auch. Man weiß ja, dass der zugriff erlaubt ist. Rechte werden nur geprüft, wenn ein Segment geladen wird und sonst nie. Der Segmentcache ist sehr wichtig, wiel dort Basis und Länge stehen. Stell dir vor die müsstest du bei jedem Zugriff auf das Segment aus dem Speicher holen. Hier gibts ncoh was zum Inhalt der Caches: http://www.x86.org/ddj/aug98/aug98.htm

    **
    welche gründe kann es jetz noch geben, doch pic zu erzeugen? bitte um anregungen und meinungen 🙂 ich hoffe, ich hab nicht vollkommenen blödsinn geschrieben.
    **

    Effizienz: Segmentzugriffe sind wie oben hoffentlich bessre geklärt langsamer 😉
    Portabilität: Segmente gibts nur auf x86

    [ Dieser Beitrag wurde am 02.06.2003 um 17:45 Uhr von TriPhoenix editiert. ]



  • ich muss dich da leider enttäuschen. die rechte werden bei JEDEM zugriff getestet. das steht auch bei deinem artikel drin und auch bei dem manual von intel. das einzige was nur beim laden geprüft wird, ist die tatsache, ob es ein codesegment oder datensegment ist und das geht fix. aber nicht die rechte, die werden immer geprüft! wozu wäre sonst der cache? das steht auch in dem artikel drin. dh ich versteht es so.

    ein zitat:

    The segment access rights and segment limit are also contained in the descriptor table. The microprocessor would need to access those structures for each memory access. These descriptor-table values reside in memory, where accesses tend to be slow when compared to accesses within the microprocessor. Therefore, without an internal segment-descriptor cache to cache these values, each memory access would implicitly require many other accesses to memory.

    hier steht eindeutig, dass der cache dazu da ist, um nicht bei beidem jedes mal die gdt anzusprechen! dh das wird bei vor JEDEM ausführen eines befehls gemacht. ob der jetz near oder far ist oder was auch immer.

    => der einzige unterschied ist der zugriff auf die gdt. und ich glaube echt, dass der schneller geht als der zugriff auf die gdt. oder mindestens gleich schnell.

    schon einmal hast du mir das gesagt, damals hab ich es dir nicht geglaubt, jetz glaub ich es erst recht nicht.

    noch einmal kurz gesagt: ein cache ist dazu da, um schnell auf was zuzugreifen. wenn ich auf nichts zugreifen will, dann brauch ich auch keinen cache. ist doch logisch oder?

    björn



  • Original erstellt von <mastamind>:
    **noch einmal kurz gesagt: ein cache ist dazu da, um schnell auf was zuzugreifen. wenn ich auf nichts zugreifen will, dann brauch ich auch keinen cache. ist doch logisch oder?
    **

    Ja, aber das was man zugreifen will sind base + limit, weil man die jeden zugriff braucht. Das mit den Rechten kommt mir suspekt vor, weil es nur beim laden eines segmentes überhuapt die chance eines fehlgeschlagenen Zugriffes gibt...mal gucken...



  • So, nochmal nachgecheckt. Intel Manual 3, 4.10.

    Der Prozi prüft jedes mal den Segment-Typ, Lese/Schreibrecht und Segment Limit.
    NICHT geprüft wird dagegen der CPL/DPL/RPL, das nur beim Ladne. Also haben wir beide Recht 😉



  • jetz is die frage, ob nicht doch das prüfen von der Xpl schneller geht, als das umschreiben der funktionsadressen.
    ich kann mir einfach nicht vorstellen, dass das in der art extrem langsam ist. vor allem weil ja es doch hardwareseitig unterstützt wird. und der pic ist nur softwareseitig.

    wie könnte man das jetz testen, ob das eine oder ander schneller ist, oder ob sie gleich schnell sind? ich würde eben das os mit segmentierung aufbauen. ist das denn wirklich ein so großer nachteil?

    wenn das ändern des cs (mit prüfen und allem) jetz zb 50 prozessor takte braucht, dann würden 1000 solche aufrufe eine milisekunde brauchen bei einem 500 mhz prozessor. nachdem ich davon ausgehen kann, dass es schon 10 ghz rechner gibt, wenn das os halbwegs verwendbar, glaube ich, dass das dann doch einfacher ist und weniger programmierarbeit... was meinst du?

    eine zusatz idee: ich möchte die api aufrufe mit call gates realisieren. dazu braucht man auf jeden fall einen far call. im endeffekt sollte das so laufen, dass die syscalls nichts andere als shared libs in einem privelegierteren ring sind. der dynamische verknüpft die api aufrufe (die in der datei als normale unresolved symbols auftauchen) mit den call gates und die normalen shared libs aufrufe mit den shared lib funktionen direkt. jede shared lib bekommt ein codesegment. für preveligierte libs wird der dpl des codesegments entsprechned erhöht und ein call gate eingerichtet, so fern das gewünscht ist (das steht entweder in einer section von elf, oder das macht die lib per eigenem code und kernel api). was meinst du dazu?



  • Original erstellt von <mastamind>:
    wie könnte man das jetz testen, ob das eine oder ander schneller ist, oder ob sie gleich schnell sind? ich würde eben das os mit segmentierung aufbauen. ist das denn wirklich ein so großer nachteil?

    Das weiß ich leider auch nicht, wie man das möglichst fair testen kann 😕

    eine zusatz idee: ich möchte die api aufrufe mit call gates realisieren. dazu braucht man auf jeden fall einen far call. im endeffekt sollte das so laufen, dass die syscalls nichts andere als shared libs in einem privelegierteren ring sind. der dynamische verknüpft die api aufrufe (die in der datei als normale unresolved symbols auftauchen) mit den call gates und die normalen shared libs aufrufe mit den shared lib funktionen direkt. jede shared lib bekommt ein codesegment. für preveligierte libs wird der dpl des codesegments entsprechned erhöht und ein call gate eingerichtet, so fern das gewünscht ist (das steht entweder in einer section von elf, oder das macht die lib per eigenem code und kernel api). was meinst du dazu?

    Von der Idee ganz schön, aber ich hab wieder meine Einwände 😃

    a) Ein Interprivileg-Sprung ist noch langsamer als ein normales Segment-Laden, weil z.B. der Stack noch getauscht werden muss.
    b) Die Frage ist, was du zur priviligierten Lib machst. Du hast maximal 8192 EInträge in der GDT, doppelt soviel mit LDT. Davon geht für jede lib shconmal ein Codesegment ab. Dann für jede (!) API-Funktion noch ein Call Gate. 16284 Funktionen klingt auf den ersten Blick möchtig, aber das ganze gilt systemweit (zumindest der GDT-Anteil). Und wenn dieses Programm nun das und das nimmt und ein anderes noch das und das, dann kommt man an die 16284 ziemlich schnell ran. Sprich du könntest dir damit eine Designentscheidung machen, die später fatal sein könnte (und sowas ist nicht ganz einfach rauszunehmen). Und das ist leider ein Limit an dem man dann nicht mehr allzuviel schrauben kann. Ist halt ein Design-Risiko 🙂

    [Ja, ich bin Anti-Segmentationler 😉 ]



  • zum ersten: einen wechsel in den privilegien hab ich so und so. das hat linux auch, wenn ich int 0x80 verwende. => ein privilegien wechsel ist sowieso nötig.

    zum zweiten: das design des os is so gehalten, dass es die funktionen auf eine kleine anzahl beschränkt. die gehen mir nicht aus. privelegiert sind nur wenige funktion der libio, libfs, libnet und libhw. die libio macht den ganzen io. das sind wenige read, write, open und close funktionen. dann noch lock dazu. dann noch funktionen für speichermanagment und für prozessmanagment. dann noch funktionen zum erstellen von netzwerk sockets. das sind alles zahlen die ich an einer hand abzählen kann.

    zugriff auf die hw folgt über die libio. es gibt 2 typen von io: stream und block. hier gibt es manche syscalls doppelt.

    ich denke ich komme mit gleich vielen aus, wie linux. und das hat so weit ich weiß nicht über 256. 256 call gates. für 3 ebenen. vielleicht 4. macht 1024 call gates. und das ist nicht viel. noch mal 1024 call gates für für libs. undich denke nicht, dass je mehr als 1024 libs geladen werden. dann noch 1024 für die ldts. und nein ich denk auch nicht, dass mehr als 1024 prozesse gleichzeitig gestartet werden. es sollte hier auf multithreadet gesetzt werden. dann noch 1024 tss. und wenn das allesecht nicht ausgeht, dann verwenden wir die ldt noch dazu.

    wieder ein satz: ich glaube nicht, dass mir die anzahl der descriptoren ausgeht, aber vielleicht fallen dir noch welche ein...

    björn



  • Original erstellt von <mastamind>:
    zum ersten: einen wechsel in den privilegien hab ich so und so. das hat linux auch, wenn ich int 0x80 verwende. => ein privilegien wechsel ist sowieso nötig.

    zum zweiten: das design des os is so gehalten, dass es die funktionen auf eine kleine anzahl beschränkt. die gehen mir nicht aus. privelegiert sind nur wenige funktion der libio, libfs, libnet und libhw. die libio macht den ganzen io. das sind wenige read, write, open und close funktionen. dann noch lock dazu. dann noch funktionen für speichermanagment und für prozessmanagment. dann noch funktionen zum erstellen von netzwerk sockets. das sind alles zahlen die ich an einer hand abzählen kann.

    zugriff auf die hw folgt über die libio. es gibt 2 typen von io: stream und block. hier gibt es manche syscalls doppelt.

    Okay, du willst also nur die Syscalls anstatt über nen Interrupt über call gates machen. Das ist vertretbar 🙂

    ich denke ich komme mit gleich vielen aus, wie linux. und das hat so weit ich weiß nicht über 256. 256 call gates. für 3 ebenen. vielleicht 4. macht 1024 call gates. und das ist nicht viel.

    Hm, inwiefern für 3 bzw. 4 ebenen? Kann mir gerade nichts drunter vorstellen 🙂

    noch mal 1024 call gates für für libs. undich denke nicht, dass je mehr als 1024 libs geladen werden.

    Also nur ein call gate pro library oder wie?

    dann noch 1024 für die ldts. und nein ich denk auch nicht, dass mehr als 1024 prozesse gleichzeitig gestartet werden. es sollte hier auf multithreadet gesetzt werden. dann noch 1024 tss. und wenn das allesecht nicht ausgeht, dann verwenden wir die ldt noch dazu.

    Das kannst du ja ganz leicht umschiffen indem du immer nur für den laufenden Prozess eine LDT bzw. ein TSS bereithältst.

    wieder ein satz: ich glaube nicht, dass mir die anzahl der descriptoren ausgeht, aber vielleicht fallen dir noch welche ein...

    Wenn du dich wirklich nur auf System calls beschränktst, nein. Ich hatte gedacht, du wolltest wesentlich mehr libraries via callgates laufen lassen, die C-library z.B. 🙂

    [ Dieser Beitrag wurde am 03.06.2003 um 01:52 Uhr von TriPhoenix editiert. ]



  • für die 3 cpls. nicht jede ebene muss/daarf alle syscalls verwende. ich möcht zu 0 und 3 auch 1 und 2 verwenden, in denen das dateisystem und der netzwerk service daemon laufen. diese brauchen unter umständen eigene call gates, dürfen dafür auf manche syscalls direkt ohne call gate zugreifen. aber auch hier werden es dann zusammen kaum mehr als 1024 sein.

    pro lib gibts ein codesegment. und pro syscall ein call gate, da der offset der funktion im cs nicht als call parameter übergeben wird (der übergebene wird ignoriert) sondern im call gate drin steht.

    ich denke, das tss und die ldt jedesmal zu ändern is langsamer, als wenn sie immer drin stehen. und ich denke so leicht gehen mir die descs dann nicht aus. wenn doch kann man das dann imme noch dynamisch machen.

    ja ich will nur die calls über call gates machen, die auf funktionen in anderen ringen zugreifen.

    wie ist also deine allgemeine meinung zu diesem design? es ist zb ziemlich dynamisch und man kann dadurch sehr viel sogar zur laufzeit am system ändern, und trotzdem ist es extrem sicher.

    björn



  • Original erstellt von <mastamind>:
    ich denke, das tss und die ldt jedesmal zu ändern is langsamer, als wenn sie immer drin stehen. und ich denke so leicht gehen mir die descs dann nicht aus. wenn doch kann man das dann imme noch dynamisch machen.

    Linux machts z.B. auch so, ich denke nicht, dass es allzu ineffizient ist, falls man das task switching sowieso manuell macht anstatt via hardware/TSS.

    wie ist also deine allgemeine meinung zu diesem design? es ist zb ziemlich dynamisch und man kann dadurch sehr viel sogar zur laufzeit am system ändern, und trotzdem ist es extrem sicher.

    Im Prinzip eine schöne Ausnutzung des Intel Modells. Ich persönlich bevorzuge bei zwei Leveln zu bleiben und die Schutzvorrichtungen über Page-Tables zu machen, aber das ist vermutlich ein bisschen mein Geschmack 🙂 Die Frage ist noch, was dir diese Sicherheit bringt. Wenn du einen Bug im Kernel-Code hast, hast du sowieso ein Problem. Und wenn z.B. die libfs für Dateisystemunterstützung abstürzt, hilft es dir auch nur bedingt weiter, dass der Kernel noch weiterlaufen kann. Den Schutz vor User-Programmen hat man auch in einem 2-Ring-Modell. Und ob man nun die System calls über call gates oder Interrupts regelt ist auch pure Geschmackssache, das macht keinen großen Unterschied. Und man müsst enoch gucken, inwiefern da die C-COmpiler mitspielen, die ja ansich auf einem flat model arbeiten. Aber ich denke, dass das Problem nicht zu schwer ist.
    Kurzum: wenn du das Modell schön findest, versuch dich dran, großartig Nachteile sollte es vermutlich nicht bringen (auch wenn ich die Vorteile ebenso nur begrenzt sehe ;)). Ich mache es nicht so, weil es mir nicht so gefällt 🙂 Aber wie gesagt, am Ende alles eine Frage der Ästhetik.


Anmelden zum Antworten