Lowlevel

Lowlevel => Lowlevel-Coding => Thema gestartet von: KtmnjjpfjsFvzG am 23. January 2013, 15:08

Titel: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 23. January 2013, 15:08
Ich bin mittlerweile bei Teil 5 des Tutorials angelangt und weiß nun wirklich überhaupt nicht mehr weiter.

Ich verstehe nicht, wozu die GDT überhaupt genutzt wird bzw. von wem.
Zweite Frage: Kann man quasi gleichzeitig im Protected Mode und im Real Mode sein?
Woran erkenne ich, in welchem Mode ich gerade bin?

OK, die GDT besteht aus Segementen, in die ich irgendwas reinschreiben muss - aber WIESO? Informationen WORÜBER?
Titel: Re: Global Descriptor Table
Beitrag von: kevin am 23. January 2013, 15:49
Ich verstehe nicht, wozu die GDT überhaupt genutzt wird bzw. von wem.
Vom Prozessor, um virtuelle Speicheradressen in physische (bzw. erst einmal lineare) umzurechnen. Alle Adressen, mit denen ein Programm hantiert, sind in der Regel virtuelle Adressen. Jeder Speicherzugriff, den du machst, geht über eine virtuelle Adresse, die aus Segment und Offset besteht. In Assembler kannst du das Segment explizit dazuschreiben (z.B. %fs:%eax), aber wenn du es nicht tust, dann gelten je nach Befehl und Register %ds, %es oder %ss als Default.

Weil du aber im Moment gar nichts umrechnen willst, definierst du einfach nur ein einziges großes Segment, das den kompletten Speicher abdeckt.

Zitat
Zweite Frage: Kann man quasi gleichzeitig im Protected Mode und im Real Mode sein?
Ich ignoriere das "quasi" mal und sage nein.

Zitat
Woran erkenne ich, in welchem Mode ich gerade bin?
Der Protected Mode wird aktiviert, wenn cr0.PE gesetzt ist.

Zitat
OK, die GDT besteht aus Segementen, in die ich irgendwas reinschreiben muss - aber WIESO? Informationen WORÜBER?
Die GDT definiert die Segmente. Das heißt, sie legt für jedes Segment fest, was für einen Typ es hat, wo im Speicher es liegt und wie groß es ist.
Titel: Re: Global Descriptor Table
Beitrag von: Svenska am 23. January 2013, 15:51
Ich verstehe nicht, wozu die GDT überhaupt genutzt wird bzw. von wem.
Die GDT ist notwendig, um Segmentierung zu implementieren. Das Tutorial nutzt allerdings keine vollständige Segmentierung, deswegen muss sie trotzdem eingerichtet werden.

Zweite Frage: Kann man quasi gleichzeitig im Protected Mode und im Real Mode sein?
Nein. Es gibt allerdings den V86-Mode, mit dem man innerhalb des Protected Modes einen Task erzeugen kann, dem eine Real-Mode-artige Umgebung bereitgestellt wird.

Woran erkenne ich, in welchem Mode ich gerade bin?
Da gibt es so ein Bit in CR0, das setzt man für den PM und löscht es für den RM - man darf es auch lesen.

OK, die GDT besteht aus Segementen, in die ich irgendwas reinschreiben muss - aber WIESO? Informationen WORÜBER?
Wieso? Damit die CPU weiß, welche Segmente es wo im (linearen) Adressraum gibt. Worüber? Anfang, Ende, Typ, Zugriffsberechtigungen...

edit: zu spät...

Gruß,
Svenska
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 23. January 2013, 15:53
um virtuelle Speicheradressen in physische (bzw. erst einmal lineare) umzurechnenOkay, aber wozu braucht man das?
Titel: Re: Global Descriptor Table
Beitrag von: Jidder am 23. January 2013, 16:12
Okay, aber wozu braucht man das?
Die Idee der Segmentierung ist den Speicher zu unterteilen und jedem Programm ein Teil davon zu geben. Für jedes Programm gibt es dann einen Eintrag (= Deskriptor) in der GDT, der die Adresse und Größe X des Speicherbereichs (= Segment) enthält. Während ein Programm läuft, sind die Segmentregister mit entsprechenden Selektoren geladen, die besagen, welcher Eintrag genutzt werden soll. Aus Sicht des Programms beginnt der Speicher bei 0 und hat die Länge X. Die CPU schaut dann bei einem Speicherzugriff in der GDT nach, welche Basisadresse im Deskriptor steht und addiert sie zu der vom Programm referenzierten Speicheradresse. Außerdem prüft sie, ob diese Adresse innerhalb des Segments liegt. Die errechnete Adresse ist die lineare Adresse. Die kann dann genutzt werden, um auf den physischen Speicher zuzugreifen.

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). Allerdings ist die Segmentierung nicht abschaltbar, und man legt deswegen ein großes Segment an, dass bei der Adresse 0 beginnt und 4 GB groß ist (maximale Größe bei 32-Bit-Systemen). Damit ist Segmentierung im Prinzip ausgehebelt.

Woran erkenne ich, in welchem Mode ich gerade bin?
Wie man es erkennt, hat Svenska ja schon beschrieben, aber normalerweise weiß man es. Wenn der Computer gerade gestartet wurde oder dein Betriebssystem vom BIOS geladen wurde, ist das System im Real Mode. Wenn du dein Betriebssystem von einem Multiboot-kompatiblen Bootloader laden lässt, ist die CPU im Protected Mode. Und über Wechsel zwischen den Modi hast du (bzw. der Kernel) die Kontrolle.
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 23. January 2013, 16:15
Hmm, na gut, aber wenn man das machen muss, warum funktioniert es dann auch ohne GDT? Ich meine, ich hab ja noch keine, aber es läuft trotzdem!
Titel: Re: Global Descriptor Table
Beitrag von: Jidder am 23. January 2013, 16:18
Der Bootloader hat bereits eine eingerichtet. Die solltest du allerdings nicht auf Dauer nutzen, weil (1. es der Standard (http://www.lowlevel.eu/wiki/Multiboot) so besagt und 2.) sie eventuell nicht deinen Anforderungen entspricht.
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 23. January 2013, 16:35
OK ich verstehe es so in etwa, vielen Dank :)
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 09. February 2013, 16:30
Hmmm... welche Adresse hat denn cr0(.PE) ? Ich finde das nirgendwo...
Titel: Re: Global Descriptor Table
Beitrag von: kevin am 09. February 2013, 18:15
cr0 ist ein Register, das hat keine Adresse.
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 09. February 2013, 18:26
Und wie soll ich das dann auslesen?
Titel: Re: Global Descriptor Table
Beitrag von: micha am 09. February 2013, 20:14
genauso wie du eax und so ausliest
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 09. February 2013, 20:14
Das mach ich garnicht...
Titel: Re: Global Descriptor Table
Beitrag von: micha am 09. February 2013, 20:22
cr0 kannst du in ein anderes register (z.b. eax) übertragen und dann verändern bzw. nur auslesen.
so schaltet man z.b. paging ein, indem man das bit 31 in cr0 setzt
mov %cr0, %eax
or %eax, (1 << 31)
mov %eax, %cr0
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 09. February 2013, 20:23
Okay, grundsätzliche Verständnisfrage: Was ist der Unterschied zwischen %eax und %eabx usw. und %ax und %bx?

Und was gibt es alles?

Oder kann man sich das selber aussuchen, also auch %asasfsdfsdfjlngouineruigndhiasdasdasdbobzi ????
Titel: Re: Global Descriptor Table
Beitrag von: micha am 09. February 2013, 20:28
Okay, grundsätzliche Verständnisfrage: Was ist der Unterschied zwischen %eax und %eabx usw. und %ax und %bx?
%eax ist ein 32-bit register. %ax ist ein 16bit register. also %ax ist die erste hälfte von %eax.

Und was gibt es alles?

Oder kann man sich das selber aussuchen, also auch %asasfsdfsdfjlngouineruigndhiasdasdasdbobzi ????
Selberaussuchen kann man sich das nicht. Die Register sind alle festgelegt.
Schau mal im wiki http://www.lowlevel.eu/wiki/X86#Register (http://www.lowlevel.eu/wiki/X86#Register)
und zu cr0 und so steht hier was: http://www.lowlevel.eu/wiki/Steuerregister (http://www.lowlevel.eu/wiki/Steuerregister)
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 09. February 2013, 21:25
Aha, ich verstehe.

Und ES:DI ? Was ist das und wieso hat es einen Doppelpunkt? :D

(Ich meine folgendes: "ES:DI = Zeiger auf freien Speicher für den VBE Info Block (512 Bytes)")
Titel: Re: Global Descriptor Table
Beitrag von: Svenska am 10. February 2013, 01:50
Hallo,

du fragst gerade nach den Grundlagen der x86-Architektur. Besorge dir mal ein Buch dazu (bzw. zu x86-Assembler). Da erfährst du, wie so eine CPU funktioniert, was Register eigentlich sind, wie das mit der Segmentierung ist, die sonst auf dieser Welt gestorben ist und wie der Real Mode eigentlich funktioniert. Oder lies das Wiki zu dem Thema, da steht das alles drin *hint, hint*.

ES ist ein Segmentregister und beschreibt ein Stück des physischen Speichers. DI ist ein Indexregister. ES:DI zeigt also mitten in ein Stück Speicher, CS:DI würde mitten in deinen Code zeigen und SS:DI mitten in deinen Stack (ob die Varianten so gültig sind, weiß ich aber aus dem Kopf nicht).

Gruß,
Svenska
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 10. February 2013, 11:42
So weit (ES) hab ich das ja verstanden, aber dann gibt es ja ES (2 Byte) und DI (2 Byte), aber was soll der Doppelpunkt?
Titel: Re: Global Descriptor Table
Beitrag von: Svenska am 10. February 2013, 13:49
Segment override.
Titel: Re: Global Descriptor Table
Beitrag von: kevin 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.
Titel: Re: Global Descriptor Table
Beitrag von: üäpöol 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?
Titel: Re: Global Descriptor Table
Beitrag von: KtmnjjpfjsFvzG am 17. February 2013, 17:34
Keine Ahnung, aber besteht der Schutz nicht trotzdem noch, auch bei flexiblen Sementgrößen?
Titel: Re: Global Descriptor Table
Beitrag von: üäpöol 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.
Titel: Re: Global Descriptor Table
Beitrag von: XanClic 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:
Titel: Re: Global Descriptor Table
Beitrag von: üäpöol 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.
Titel: Re: Global Descriptor Table
Beitrag von: XanClic 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.
Titel: Re: Global Descriptor Table
Beitrag von: Svenska 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
Titel: Re: Global Descriptor Table
Beitrag von: kevin 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.
Titel: Re: Global Descriptor Table
Beitrag von: Svenska 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 (http://gcc.gnu.org/onlinedocs/gcc/Named-Address-Spaces.html)). 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
Titel: Re: Global Descriptor Table
Beitrag von: Dimension 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.
Titel: Re: Global Descriptor Table
Beitrag von: kevin am 18. February 2013, 20:16
Außer du benutzt Pointer, dann gehen sie über ds/es.
Titel: Re: Global Descriptor Table
Beitrag von: Dimension 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.
Titel: Re: Global Descriptor Table
Beitrag von: kevin 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)