Autor Thema: Global Descriptor Table  (Gelesen 17136 mal)

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #20 am: 10. February 2013, 14:01 »
Eine virtuelle Adresse auf x86 besteht aus einem Segment(selektor) und einem Offset in dieses Segment. Das Segment steht vor dem Doppelpunkt, das Offset danach. Wenn du das Segment weglässt, wird ein Default benutzt - normalerweise %ds, bei Adressierung über %esp und %ebp ist es %ss und beim Zieloperand von manchen Instruktionen ist es %es statt %ds. Dass das nicht bei jedem einzelnen Speicherzugriff dabeisteht, ist also reine Bequemlichkeit, die nur möglich ist, weil der Default passt.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 17. February 2013, 17:11 »
Hi,
Die Idee dahinter ist, die Programme voreinander zu schützen (daher das "Protected" in "Protected Mode"), indem man die Segmente so einrichtet, dass sie sich nicht überlappen. Programme die versuchen über die Segmentgrenzen hinaus auf Speicher zuzugreifen, werden vom Betriebssystem daran gehindert. So wie ich das beschrieben habe, macht es allerdings niemand und die Segmentierung wird von kommerziellen Systemen ignoriert. Stattdessen wird Paging eingesetzt, das flexibler ist (keine feste Größe der Segmente).
ich finde die Idee eigentlich ziemlich interessant, vor allem weil die Segmente und somit eigentlich auch der PM extra dafür gemacht sind. Warum wird das denn nicht gemacht? Nur weil Paging flexibler ist?

KtmnjjpfjsFvzG

  • Beiträge: 111
    • Profil anzeigen
Gespeichert
« Antwort #22 am: 17. February 2013, 17:34 »
Keine Ahnung, aber besteht der Schutz nicht trotzdem noch, auch bei flexiblen Sementgrößen?

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #23 am: 17. February 2013, 18:32 »
Das schon, aber gibt es auch noch andere Nachteile? In der Implementierung dürfte es ja recht einfach sein und für mich zum Beispiel wäre der Nachteil, dass ich die Größe der Segmente nicht flexibel wählen darf, nicht so schlimm.

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #24 am: 17. February 2013, 20:15 »
In der Implementierung dürfte es ja recht einfach sein
Der war gut. Ich wünsche viel Spaß. :wink:

üäpöol

  • Beiträge: 110
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 17. February 2013, 20:20 »
:D Okay. Wieso reicht es denn nicht für einen Task ein Segment zu erzeugen?
Sorry, ich bin Anfänger.

XanClic

  • Beiträge: 261
    • Profil anzeigen
    • github
Gespeichert
« Antwort #26 am: 17. February 2013, 20:50 »
Normalerweise will jeder Task seine Segmente dynamisch vergrößern, für Heap und so. Irgendwann kommen sich Segmente dadurch zwangsläufig in die Quere und dann beginnt der Spaß, da muss man die im Speicher hin und her schieben, dabei immer alle Datei mitkopieren, die Deskriptortabellen anpassen (sei es GDT, sei es LDT)…

Das Problem hat man mit Paging halt nicht, weil lineare Adressen nichts mit den physischen¹ zu tun haben. Die virtuellen Adressen der Segmentierung hingegen haben durchaus was mit den linearen² zu tun, weil sie kontinuierlich abbilden, von einer Basisadresse eine bestimmte Anzahl Bytes. Deshalb kann da überhaupt irgendwas kollidieren.

Das heißt auch, wenn man einem Programm ein Segment zuweist, dann muss der dahinter liegende physische Speicher auch tatsächlich existieren. Das ist bei Paging natürlich ganz anders, da muss vom gesamten Adressraum im Prinzip gar nichts physisch da sein (weil der Adressraum ja aus vielen einzelnen Segmenten (den Pages) besteht, die jeweils entweder da sind oder nicht). Man kann das gleiche Prinzip bestimmt auch bei Segmentierung anwenden, nur heißt das dann natürlich, dass jedes Programm ganz viele Segmente bekommt (bspw. für jedes Objekt eins). Und da hat man dann viel Spaß, einen Compiler zu finden, der so ein Speichermodell unterstützt.

tl;dr: Mit Segmentierung hat man nur Ärger, weil man entweder nicht mal eben irgendeinem Programm mehr Speicher zuweisen kann (bestimmt gibts noch andere Gründe), oder zumindest keinen Compiler findet, der sie unterstützt, wenn man sie ordentlich machen will.


¹Aus einer virtuellen Adresse wird mit Segmentierung eine lineare Adresse und daraus mit Paging eine physische. „Ohne“ Segmentierung sind also alle linearen Adressen äquivalent zu den virtuellen.
²Ohne Paging sind alle linearen Adressen äquivalent zu den physischen.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 17. February 2013, 23:51 »
Der Hauptvorteil von Segmenten ist, dass jedes Segment mit einer Adresse Null anfängt und man damit positionsunabhängigen Code geschenkt bekommt (man schreibt in CS das Segment rein und linkt ab Adresse Null). Damit kann man Programme an beliebige physische Adressen laden, das korrekte Segment ins passende Register schreiben und fertig. Außerdem - und das geht mit Paging nicht so schön - kann man gleiche Segmente (Bibliotheken) von verschiedenen Programmen gleichzeitig nutzen, ohne jeder Bibliothek eine eigene, in allen Tasks freie Adresse zu geben.

Ein weiterer Nachteil von Segmentierung ist, dass es nicht genug frei verfügbare Segmentregister (nur DS, ES, FS und GS) gibt. Segmentwechsel sind wegen der Berechtigungsprüfungen teuer. Ein Segment pro Objekt und jeder Objektzugriff ist teuer.

Es ist allerdings möglich, Segmentierung und Paging gleichzeitig zu benutzen. Das möchte aber vermutlich keiner von uns wirklich programmieren. :-)

Wirklich gut ist Segmentierung nur, wenn man Assembler programmiert. Compiler kommen mit fragmentierten Adressräumen nicht sonderlich gut klar.

Gruß,
Svenska

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #28 am: 18. February 2013, 13:13 »
Zitat
Wirklich gut ist Segmentierung nur, wenn man Assembler programmiert. Compiler kommen mit fragmentierten Adressräumen nicht sonderlich gut klar.
Das ist das Problem. Ein getrenntes Segment für den Stack wäre schon schick, damit hättest du garantiert jeden Stack Overflow abgefangen und müsstest nicht mit irgendwelchem halbgaren Mist wie Guard Pages anfangen, die manches abfangen und anderes nicht.

Es ist allerdings möglich, Segmentierung und Paging gleichzeitig zu benutzen. Das möchte aber vermutlich keiner von uns wirklich programmieren. :-)
Das ist die Variante, die ich persönlich interessant fände. Ohne Paging will man nicht, aber Segmente könnten schon auch ganz nett sein. Ich bin bisher nur noch nicht dazu gekommen, sowas zu implementieren, und bei meiner aktuellen OS-Dev-Aktivität wird sich das wohl so bald auch nicht ändern. Wobei ich wahrscheinlich auch nur die Schmalspurvariante umsetzen würde, wo cs/ds/ss gleiche Basis und Limit haben, damit ich nicht auch noch einen Compiler schreiben muss. In dem Fall wäre das interessante daran nur noch, dass man mehrere Prozesse in einem Adressraum haben kann. Damit kann man sich vermutlich ein paar TLB-Flushes sparen, und vielleicht würde es sich auch für IPC noch als praktisch erweisen.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 18. February 2013, 13:58 »
Hallo,

die Idee finde ich auch sehr schön, aber leider kommt sie 25 Jahre zu spät. Es gibt keinen mir bekannten 64-Bit-Prozessor, der Segmentierung unterstützt und die ganze Arbeit der Prozessorhersteller in Beschleunigung ging immer von Flat Memory aus, weil das jeder(TM) nutzt. Betriebssysteme, die anders funktionieren (z.B. OS/2), lassen sich auch nur sehr schlecht simulieren.

Wobei ich wahrscheinlich auch nur die Schmalspurvariante umsetzen würde, wo cs/ds/ss gleiche Basis und Limit haben, damit ich nicht auch noch einen Compiler schreiben muss.
Mich hat überrascht, dass der gcc scheinbar verschiedene Adressräume gelernt hat. Siehe hier). Der avr-gcc ab 4.7.0 unterstützt jetzt "__flash", welches andere Instruktionen zum lesen benutzt und endlich dieses PROGMEM/pgm_read_{byte,word}-Hacking überflüssig mache.

Vielleicht könnte man das dem Compiler irgendwie beibringen und dann sinnvoll nutzen... ich kann es nicht. :P

Gruß,
Svenska

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 18. February 2013, 19:59 »
cs/ds/ss gleiche Basis und Limit haben, damit ich nicht auch noch einen Compiler schreiben muss.
GCC adressiert lokale Variablen über esp/ebp, diese gehen über ss.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 18. February 2013, 20:16 »
Außer du benutzt Pointer, dann gehen sie über ds/es.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Dimension

  • Beiträge: 155
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 19. February 2013, 12:28 »
Außer du benutzt Pointer, dann gehen sie über ds/es.
Zumindest der Code sollte sich doch in ein eigenes Segment legen lassen. Genau genommen ist ein Overflow im Stack (ROP) wohl ähnlich schwerwiegend wie ein Overflow aus dem Stack in das Datensegment hinein.

Da fällt mir ein: was passiert, wenn ESP kleiner 0 wird, gehts dann bei 4g weiter oder gibts eine Exception? Kann man x beliebig wählen, hat man mit [esp+x] und Overflow bei 4g ohnehin das ganze Segment verfügbar, nehme ich an.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #33 am: 19. February 2013, 17:37 »
Das gibt einen Wraparound, man kommt also immer an die vollen 4 GB ran (außer natürlich das Segmentlimit sagt was anderes)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

 

Einloggen