Autor Thema: OS für Plattform mit Segmentierung  (Gelesen 20509 mal)

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #20 am: 12. August 2009, 11:46 »
Zitat
Ja, es wird sogar in den nächsten C++ Standard kommen.
Dann werden sich die verantwortlichen bestimmt auch überlegt haben wie man das in einem heute gängigen OS (alle mir bekannten nutzen Flat-Memory und Paging) umsetzt.
Eventuell wird dann nicht mehr eine Page-Table pro Task sondern eine pro Thread benötigt, und dann soll noch mal jemand behaupten das Paging keine Performance kostet. :wink:
Es wird afaik über Segmentation erreicht. Sowohl unter x86 als auch unter x64. Wie gesagt, der einzige Grund wo es m.E. von Vorteil ist.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #21 am: 12. August 2009, 12:43 »
Hallo,


Zitat
Naja, der Bedarf, das Linkregister auf dem Stack zu parken, entsteht sobald eine Funktion eine weitere Funktion aufrufen will.
Ja, aber man muss es nicht sofort nach dem Einsprung in eine Funktion tun sondern kann es auf später, kurz vor dem Aufruf der Unterfunktion, erledigen. Damit ist die Rücksprungadresse nicht so lange auf dem Stack und daher weniger, eventuell gar nicht, angreifbar. Ob heutige Compiler sowas können weiß ich aber nicht.
Außerdem gibt es viele kleine Funktionen die gar keine Unterfunktionen aufrufen, die sparen sich mindestens 2 Speicherzugriffe.


Zitat
Es wird afaik über Segmentation erreicht.
:-D

Zitat
als auch unter x64
Da bleiben ja nur FS und GS als verstümmelte "Segment-Imitatoren" übrig. Viel Spaß.

Was ist dann eigentlich mit ARM, ALPHA, AVR32, MIPS, Microplace, Itanium, PowerPC ...... ??
Ob die dann wohl nicht mehr C++ kompatibel sind?


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 #22 am: 12. August 2009, 19:15 »
Da bleiben ja nur FS und GS als verstümmelte "Segment-Imitatoren" übrig. Viel Spaß.
Die Basisadresse ist da. Also genauso viel wie bei den tollen Real-Mode-Segmenten, die im hochgelobten DOS gestern noch Spaß gemacht haben. ;)

Zitat
Was ist dann eigentlich mit ARM, ALPHA, AVR32, MIPS, Microplace, Itanium, PowerPC ...... ??
Ob die dann wohl nicht mehr C++ kompatibel sind?
Wie kommst du auf das schmale Brett, auf diesen Architekturen könnte man keinen TLS implementieren? Ich kenne die Architekturen kaum mehr als vom Namen, insofern kann ich dir nicht sagen, wie man es im einzelnen macht. Aber PPC zum Beispiel hat Segmentierung. Sogar extra für dich mit fixer Größe. ;)
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 #23 am: 12. August 2009, 21:27 »
Hallo,


Zitat
Die Basisadresse ist da.
Mehr aber auch nicht, also doch nur "Segment-Imitatoren".


Zitat
Wie kommst du auf das schmale Brett, auf diesen Architekturen könnte man keinen TLS implementieren?
Nunja das wird wohl schon irgendwie gehen. Das einzigste was mir dafür einfällt ist eine Page-Table pro Thread und nicht nur eine pro Task.
Die genanten Architekturen, so wie fast alles was zwischen 0 und 1 unterscheiden kann, haben alle (Okay der PowerPC hat wohl sowas ähnliches wie Segmentierung aber nichts berauschendes und ob der Itanium in seiner HW-x86-Emulation den 386-PM komplett unterstützt weis ich nicht) "nur" Paging und auf allen genanten (und etlichen mehr) läuft Linux und noch ne Reihe anderer Embedded/Real-Time-OS welche das Paging genau so benutzen wie die meisten OSdever hier es auch tun.

Alles was im OS-Bereich Rang und Namen hat basiert auf den selben "altgedienten" bzw. "altbewährten" Speicherverwaltungskonzepten. Wird Zeit das da mal was neues kommt. :wink:


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

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #24 am: 12. August 2009, 22:55 »
Zitat
Die Basisadresse ist da.
Mehr aber auch nicht, also doch nur "Segment-Imitatoren".
Exakt das was man für TLS eben braucht (und dafür ein Pointer auf eine Kernelstruktur zu speichern kann man gs mut swapgs dann auch recht gut hernehmen).

Man kann mindestens auf folgende Arten TLS implementieren (abgesehen von den bereits genannten):
* ein Syscall get_thread_storage() der einen Zeiger zurückgibt auf den TLS-Bereich. Das wird wohl auf jeder Architektur gehen, halt halt den Syscall-Overhead was ja ziemlich viel sein kann
* Stacks zB an 4MB alignen und die obersten 4/8 Byte für einen Zeiger auf den TLS nutzen, das geht ohne das man Paging bei jedem Thread verschieden macht und es ist sicherlich extrem fix
* wenn ich schon die Pagetables verbiegen will, dann mach ich das doch aber nur auf einer Seite und lege da eben einen Zeiger auf den eigentlichen TLS ab, alles andere würde das Programmiermodel beeinträchtigen (du kannst keine Zeiger auf TLS anderer Threads verwenden)

Außerdem glaube ich kaum, dass das C/C++-Standardkomitee Sachen standardisieren würde, welche auf relevanten Systemen nicht implementierbar sind. Die Frage des Aufwands der Implementation ist natürlich eine andere, aber da muss man auch ganz klar sehen, dass Hardware dafür da ist Software laufen zu lassen, insofern hat es das zu unterstützen was sinnvoll/möglich ist in angemessener Geschwindigkeit, va. wenn das andere Platformen ohne Probleme hinkriegen.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #25 am: 14. August 2009, 19:31 »
Hallo,


entschuldige mein Delay, aber ich wollte mich erst mal etwas genauer in das Thema TLS einlesen. Interessant war da vor allem der Artikel in der englischen Wikipedia und dessen externe Links. http://en.wikipedia.org/wiki/Thread-local_storage

Ich bin z.B. davon ausgegangen das ein Pointer in den TLS immer auf den TLS des aktuellen Threads verweisen soll und nicht auf den TLS eines anderen Threads oder das der TLS selber in dem Segment, auf x86 eben GS, steckt.
In Wirklichkeit gibt es nur einen Thread-Spezifischen Pointer auf eine jeweilige Struktur (im normalen Adress-Raum) die den TLS für alle Threads auf die gleiche Weise aber mit verschiedenem Inhalt beschreibt. Sieht insgesamt einigermaßen kompliziert aus. Gleich im ersten externen Link des Wikipedia-Artikels wird das für einige Plattformen konkret beschrieben und ich hab jetzt auch eine Idee wie ich das für mein OS umsetzen möchte. Wenn es wirklich demnächst in den C/C++-Standard rein kommt wird wohl früher oder später die Notwendigkeit auftauchen das zu unterstützen wenn ich mal ein Programm portieren möchte.


Da Du die Stacks ansprichst, wie machst man das eigentlich in einem Flat-Memory-System?
Die einzelnen Stacks der verschiedenen Threads müssen ja hintereinander in den einen  Adress-Raum des Tasks gelegt werden, oder irre ich da? Wie sieht das aus wenn ein Thread dann mehr Stack braucht als Platz bis zum nächsten Stack eines anderen Threads ist? Merkt das das OS eigentlich zuverlässig?
Für die Segmentierung hab ich mir gedacht ich erstelle für jeden Thread ein eigenes lokales Segment mit einer kleinen Anfangsgröße und wenn der Stack wächst, auf meiner Plattform übrigens hin zu den höheren Adressen bzw. Offsets, wird das Segment vom OS vergrößert (eventuell nur bis zu einem definiertem Maximum). Beim Zugriff auf Offsets die das Stack-Segment-Limit überschreiten wird eine Exception ausgelöst aber vom OS speziell behandelt da ja wachsender Stack normal ist. Falls das Segment an seiner momentanen Position im Linearen-Adress-Raum keinen Platz mehr hat muss es eben schnell umgelegt werden. Ob ich ein Schrumpfen, wenn das OS beobachtet das der Stack eine Weile lang wieder kleiner ist, implementiere weis ich noch nicht, währe aber ne nette Idee.


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

bluecode

  • Beiträge: 1 391
    • Profil anzeigen
    • lightOS
Gespeichert
« Antwort #26 am: 14. August 2009, 23:48 »
Die einzelnen Stacks der verschiedenen Threads müssen ja hintereinander in den einen  Adress-Raum des Tasks gelegt werden, oder irre ich da?
"Hintereinander" ist da eher relativ, man kann ja speicher zwischendrin ungemappt lassen (zB. 4MB virtuell für einen Stack vorsehen, davon die obersten 4kB mappen), stört ja keinen. Wenn dann bei einem Thread der Stack zu klein wird kommt beim Zugriff eben ein Pagefault und man mappt ein bisschen mehr.
lightOS
"Überlegen sie mal 'nen Augenblick, dann lösen sich die ganzen Widersprüche auf. Die Wut wird noch größer, aber die intellektuelle Verwirrung lässt nach.", Georg Schramm

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #27 am: 15. August 2009, 11:05 »
Hallo,


Zitat
man kann ja speicher zwischendrin ungemappt lassen
Ahja, reicht das als Sicherheitsmaßnahme gegen einen Stackoverflow wirklich aus?
Nehmen wir mal an man würde die Stacks an 0xBFFFFF00, 0xBEFFFF00 und 0xBDFFFF00 (32Bit OS) jewails nach unten wachsen lassen, also alle 16MByte einen Stack. Die Pages an  0xBF000000, 0xBE000000 und 0xBD000000 jeweils ungemappt lassen. Wenn dann der mittlere Thread (also der Thread dem der mittlere Stack gehört) eine Funktion wie die folgende aufruft
void foo()
{
  int z;
  char test[16777216];

  z = 1234567;
}
dann währe die 1234567 im Stack eines anderen Thread gelandet und niemand hätte das bemerkt. Eventuell müsste man die Deklaration von z und test vertauschen damit z an der kleineren Adresse steht aber prinzipiell sollte diese Untat funktionieren, oder hab ich da was übersehen?
Das mit den ungemappten Lücken, zwischen den Stacks, wirkt ja nur wenn da auch jemand drauf zugreift. Niemand hindert das User-Space-Programm am überspringen dieser Lücke.

Was passiert eigentlich wenn ein Thread berechtigt mehr Stack benötigt, also auf so eine ungemappte Lücke zugreift? Wird dann einfach der komplette Task gekillt?
Das OS kann ja nicht entscheiden ob da ein böser Programmierer, eine fehlerhafte Endlos-Rekursion oder einfach ganz normaler Stackverbrauch vorliegt.


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

Termite

  • Beiträge: 239
    • Profil anzeigen
Gespeichert
« Antwort #28 am: 15. August 2009, 11:59 »
Hi

Ich hab jetzt nicht wirklich alles gelesen.

aber auf gewissen hw achritekturen, gerade im embedded bereich, gibt es gewisse hw seitige enschränkungen, die dynamisch wachsende Task stacks nicht ermöglichen. Ohne MMU oder etwas ähnlichem wird das nicht gehen.
Auf dem PC kann jede Task / Thread / Prozess seinen eigene Virtuelle Addressierung haben, die von der MMU wieder auf die Physikalische umgerechnet wird. Bei systemen mit MMU kann keine Task speicher einer anderen Task gefährden.

beim PC wird eine Ausnahme geworfen, wenn der Stack überläuft/ bzw auf Virtuelle Addresen zugegriffen wird für die kein Mapping existiert. Dies veranlast das OS dann eine neue speicherseite in den Stackadressraum einzuhängen.

und einfach irgend was durch die gegendschieben z.B. ganze Segmente geht auch nicht immer (absolute Addresierun / relative Addresierung). Du sagst, du hast eine segmentierte addressieren, dann funktioniert das nur solang dein  Programm nicht über eine Segment grenze hinweg springen muss.

Im embedded bereich wird normalerweise die Stacksize jeder Task zur designzeit definiert, zur laufzeit wird da nichts mehr vergrössert oder verkleinert, da die HW wie oben beschrieben das meist gar nicht ermöglicht. bzw das meist eine zusätzliche fehlerquelle ist die mang gerne ausschliesen möchte. Das einzige, was man machen kann, ist beim taskwechsel zu prüfen, ob der Taskblock inclusieve definiertem stack noch in ordnung ist. (magic number) wenn nicht kann das os die verarbeitung einstellen, da es ja nicht wisssen kann, was genau alles Überschrieben wurde.


Termite

  • Beiträge: 239
    • Profil anzeigen
Gespeichert
« Antwort #29 am: 15. August 2009, 12:37 »
wobei mir gerade einfällt in einem FPGA könnte man die MMU ja extern nachrüsten und über einen interrupt an mit der CPU verbinden.

welches FPGA willst du eigentlich einsetzen, nur interesse halber.
den namen der soft cpu wolltest du ja nicht preisgeben. Ich nehme aber an, das es was exotisches ist, bzw ganz spezielle eigenschaften. müsste eine 16 Bit cpu sein, da sonst die segmentierung wenig sinn machen würde.

gruss

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #30 am: 15. August 2009, 14:20 »
Hallo Termite,


Zitat
aber auf gewissen hw achritekturen, gerade im embedded bereich, gibt es gewisse hw seitige enschränkungen, die dynamisch wachsende Task stacks nicht ermöglichen.
Ich persönlich bin der Meinung das das eher eine konzeptionelle Einschränkung ist. Wenn in dem einem Adress-Raum einer Task bereits dort irgendwas liegt wo ein Stack gerne hinwachsen möchte dann geht das eben nicht. Bei Segmentierung existiert das Problem nicht, die einzelnen Segmente einer Task sind, aus Sicht des Task selber, unabhängig von einander. Die Segmente haben keinen Bezug zueinander. Jedes Segment kann prinzipiell seine Größe ändern, natürlich wird das OS dem bestimmte Grenzen auferlegen, das Code-Segment z.B. wird das OS bestimmt nicht ohne triftigen Grund verändern.

Zitat
beim PC wird eine Ausnahme geworfen, wenn der Stack überläuft/ bzw auf Virtuelle Addresen zugegriffen wird für die kein Mapping existiert. ...
Das wird IMHO auf allen Plattformen, die das Paging für die Speicherverwaltung benutzen, so gemacht. Hindert das User-Space-Programm trotzdem nicht daran über eine ungemappte Lücke hinweg zu springen.

Zitat
und einfach irgend was durch die gegendschieben z.B. ganze Segmente geht auch nicht immer
Warum nicht? Das Task selber benutzt nur absolute Adressen, jede für das Task erreichbare Speicherstelle ist mit Segment:Offset eineindeutig adressierbar. Wo die Segmente im linearen Adress-Raum, oder gar im physischen Adress-Raum, liegen weis das Task nicht und genau deshalb kann das OS da schieben was es will, mit etwas Geschick sogar während das betreffende Task läuft.

Zitat
dann funktioniert das nur solang dein Programm nicht über eine Segment grenze hinweg springen muss.
Was genau meinst Du damit?
Die Daten eines Task verteilen sich über mehrere Segmente (Code, Constanten, Daten, Heap, (mehrere) Stacks und sonstiges) und jedes Segment hat eine bestimmte Größe, wenn der Code versucht ein Offset oberhalb eines Segment-Limits (oder kleiner 0) zu lesen/schreiben gibts ne Exception.

Zitat
Im embedded bereich wird normalerweise die Stacksize jeder Task zur designzeit definiert ...
Weis ich, ich hab lange im Embedded-Bereich programmiert (beruflich und privat). Im PC-Bereich wird das aber nicht gemacht. Stell Dir vor Windows würde nur 1MByte Stack erlauben. Ich kenne einige Programme die damit ein Problem hätten.


Zitat
welches FPGA willst du eigentlich einsetzen
Wahrscheinlich Spartan 6, ich möchte die einzelnen Componenten gerne mit den High-Speed-Transceivers verbinden. Ich würde gerne den Virtex 6 einsetzen aber das sprengt mein Budget.

Zitat
müsste eine 16 Bit cpu sein, da sonst die segmentierung wenig sinn machen würde.
Wieso macht Segmentierung bei einem 32/64Bit-System keinen Sinn?
Mein System wird es in beiden Varianten geben, mit identischen Segmentierungs-Fähigkeiten, und ich möchte auf jeden Fall eine PU (Paging-Unit, ich nenne das absichtlich nicht MMU) einbauen, damit kann man an einigen Stellen etwas tiefer in die Trickkiste greifen (Beispiele hab ich ja schon ein paar genannt) und vor allem swappen.


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

Termite

  • Beiträge: 239
    • Profil anzeigen
Gespeichert
« Antwort #31 am: 17. August 2009, 09:22 »
Moin

Segmentierung, so wie ich sie kenne, (C166 / 8086) hatte damals was mit dem Bedürfniss zu tun, mehr speicher zu addressieren, als mit 16 bit überhaupt möglich ist, darum wurden die Segmentregister eingeführt, durch die man  die Addressierung von 16 auf 20, 24 ,.... bit aufborehn konte. daher auch die lustigen far jumps, bei denen segmentregister und dann der IP manipuliert wird.

Bei 32 bit. ist das normalerweise fast nicht mehr notwendig. ausnahmen wie der PC der mehr als 4gb RAM addressieren soll mal ausgenommen. Beim PC gibts ja jetzt dafür die 64Bit maschienen. Im serverberich / High performenc computing waren die ja genau desewegen ja schon lange im einsatz, da dort 4gb ram schon lang nicht mehr ausgereicht haben. (alpha, ultrasparc, ... )

Bei aktuellen 80X386 CPUs gibt es ja die SegmentSelectoren. Die aber meines wiessens wieder auf einen gemeinsamen virtuellen 32Bit Addressraum zeigen. (Code, Stack, Data zusammen dürfen nicht mehr als 4gb belegen)

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #32 am: 17. August 2009, 13:24 »
Hallo,


Zitat
Segmentierung, so wie ich sie kenne, (C166 / 8086) ....
Ja, das glaube ich gern.
Kaum jemand hat sich wohl je mit den tollen Möglichkeiten beschäftigt die der PM des 286/386 bieten sollte. Im 286 ist natürlich noch die 64k-Grenze drin (die möchte ich heute auch nicht mehr haben) aber konzeptionell war der ganze PM schon sehr fortschrittlich und wurde dann im 386 mit echten 32Bit-Adressen und dem Paging komplettiert.


Zitat
Bei 32 bit. ist das normalerweise fast nicht mehr notwendig.
Natürlich gibt es ein paar Vorteile wenn man den gesamten erreichbaren Speicher mit einem "simplen" (NEAR-)Pointer erreichen kann aber es gibt eben auch ein paar Vorteile das mit einem "komplexen" (FAR-)Pointer zu machen.


Zitat
Bei aktuellen 80X386 CPUs gibt es ja die SegmentSelectoren.
Ohne das damit je was vernünftiges gemacht wurde. :-( So hatten sich die Intel-CPU-Ingenieure damals das sicher nicht vorgestellt.
Zitat
Die aber meines wiessens wieder auf einen gemeinsamen virtuellen 32Bit Addressraum zeigen.
Das machen die OS-Coder absichtlich so um damit die Segmentierung komplett auszuhebeln so als währe sie nicht da.
Ich hab mir damals (vor über 15 Jahren) ein Buch über die System-Programmierung des 386 gekauft und der Autor hat über den virtuellen (segmentierten) Adress-Raum des 386-PM regelrecht geschwärmt, angeblich unglaubliche 64TByte (also einfach 4GByte * 16384 Segmente, da ja GDT und LDT zusammen so viele fassen). Der Autor ist wohl davon ausgegangen das jemand ernsthaft versuchen würde Segmentweise zu swappen, ist zwar theoretisch möglich aber in der Praxis völliger Schwachsinn da man ja eigentlich immer mehrere Segmente (mindestens Code, Daten und Stack + OS-Kram) gleichzeitig benötigt. Ein paar Kapitel später wurde dann sehr bodenständig erklärt wie man mit dem Paging beengte Verhältnisse im physischen RAM umgeht und bei diesen Erklärungen lagen auch immer alle Segmente in einem linearen Adress-Raum.


Zitat
(Code, Stack, Data zusammen dürfen nicht mehr als 4gb belegen)
In meinem Konzept dürfen alle Tasks (und das OS) zusammen nicht über die 4GByte (zumindest in der 32Bit-Variante) hinaus wachsen da sie alle in einem einzigen linearen Adress-Raum liegen der, bei Bedarf mit Hilfe des Paging, größer sein kann als physischer RAM existiert. Ich sehe in dieser Beschränkung auch erstmal keine Probleme, mein Entwicklungsrechner z.B. kommt mit seinen physischen 1,5GByte auch aus ohne zu swappen trotz KDE usw. Spätestens auf einer vollwertigen 64Bit-Plattform ist dann auf absehbare Zeit ein gemeinsamer linearer Adress-Raum kein Problem mehr.


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

FritzS

  • Beiträge: 2
    • Profil anzeigen
Gespeichert
« Antwort #33 am: 18. August 2009, 15:19 »
Hallo Termite und erik

Ich will mal einige Aspekte aus der Sicht des Users betrachten.
Der User ist eigentlich der Compiler bzw. Linker der den ladbaren
Programmfile erstellt

- Flat-Speicher
  Adresse beginnt immer bei 0x00
  das OS gibt vor wo der Code anfängt
  das Programm muss sich selbst kümmern ob das sich
  Stack, Code und Daten nicht in die Quere kommen.
  jede Änderung des Limits kann nur das OS über Systemaufrufe
  jeder Zugriff außerhalb des Speicherlimits muss eine Exception werfen
Wenn nun dieser Speicher im Hitergrund in mehrere Pages zerteilt ist das erstmal egal. Sind aber nun mit diesen Seiten mit unterschiedlichen Attributen verknüpft (NoeXecute, ReadOnly) so ist das ziemlich merkwürdig.
- Segmentierung
  jedes Segment  beginnt auch bei 0x00
  jedes Segment soll keine Überlappung in ein anderes Segment haben
  Zugriffe außerhalb der Segmente müssen auch dann eine Exception
  werfen wenn sie in einem anderen gültigen Segment landen
far-Pointer sind eigentlich nicht nötig sondern nur Segment-Override-Präfixe

Für beide Modelle muss gelten das der schreibende Zugriff auf die
Deskriptoren eine Exception wirft (auch wenn der User sysadmin ist).

Hinter beiden Speichermodellen sollte nun noch das Paging stehen
um die Adressen in den realen Speicher zu übersetzen.

MfG
Und was hindert uns als OS-Designer daran beide Modelle im
fertigen OS anzubieten.


erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #34 am: 18. August 2009, 21:17 »
Hallo,


Zitat
Adresse beginnt immer bei 0x00
Der virtuelle Adress-Raum, im Flat-Model, beginnt zwar bei 0 aber die 0-te Page lässt man für gewöhnlich ungemappt damit ein NULL-Pointer eine Exception wirft.

Zitat
das OS gibt vor wo der Code anfängt
ja, das stimmt, kann man übrigens auch bei Segmentierung so machen

Zitat
das Programm muss sich selbst kümmern ob das sich Stack, Code und Daten nicht in die Quere kommen.
Der Linker markiert das im Executable-File als Sektionen so das diese Unterscheidung der Executable-Loader  vom OS passend treffen kann, der verteilt dann alles vernünftig im virtuellem Adress-Raum vom neu zu erstellendem Task.

Zitat
jede Änderung des Limits kann nur das OS über Systemaufrufe
jeder Zugriff außerhalb des Speicherlimits muss eine Exception werfen
Im Flat-Memory-Model gibt es kein Limit. Es gibt nur ungemappte bzw. unberechtigte (z.B. Kernel-Space) Bereiche im linearen Adress-Raum eines Task.

Zitat
Wenn nun dieser Speicher im Hitergrund in mehrere Pages zerteilt ist das erstmal egal.
Davon merkt das User-Task ja gar nichts.

Zitat
Sind aber nun mit diesen Seiten mit unterschiedlichen Attributen verknüpft (NoeXecute, ReadOnly) so ist das ziemlich merkwürdig.
Das ist nicht "merkwürdig" sondern der einzigste Weg wenigstens ein bisschen Sicherheit innerhalb eines Task zu etablieren. Die Task untereinander können sich ja aufgrund der unterschiedlichen Adress-Räume eh nicht in die Quere kommen.
Diese Page-Attribute werden anhand der Sektions-Eigenschaften aus dem Executable-File passend gesetzt, sind also etwas was Compiler bzw. Linker direkt beeinflussen können.


Zitat
jedes Segment  beginnt auch bei 0x00
Beim 386-PM ja, deshalb kann man dort die Null-Pointer-Exception nicht so zuverlässig implementieren. Auf meiner Zielplatform gibt es für jedes Segment nicht nur ein individuelles Limit sondern auch ein kleines individuelles Minimum, damit werden zwar in jedem Segment ein paar (wenige) Bytes verschwendet aber dafür gibts zuverlässige Null-Pointer-Exceptions.

Zitat
jedes Segment soll keine Überlappung in ein anderes Segment haben
Richtig. Es gibt aber ein paar Ausnahmen, z.B. muss ein Debugger für das Code-Segment seines Client-Programms ein Alias-Daten-Segment (mit der selben Basis und dem selben Limit) erstellen damit er überhaupt den Programm-Code sieht (sonst gäbe es keine Disassembler-Ansicht) da das Code-Segment ja Executable-Only ist (also nicht lesbar und erst recht nicht schreibbar). Aber Teil-Überlappungen, so wie im 8086-RM, soll es aber definitiv nicht geben.

Zitat
Zugriffe außerhalb der Segmente müssen auch dann eine Exception werfen wenn sie in einem anderen gültigen Segment landen
Was genau meinst du damit?
Das User-Task weis nicht wo seine Segmente im linearen Adress-Raum liegen (das kann sich sogar mit der Zeit ändern) also gibt es keine Beziehungen der Segmente untereinander. Die CPU selber wird jedenfalls nicht prüfen ob ein Offset das aus einem Segment raus ragt in ein anderes rein fällt.

Zitat
far-Pointer sind eigentlich nicht nötig
Irgendwie muss ja ein allgemein geltendes Stück Programm-Code einem Pointer ansehen welches Segment gemeint ist. Dazu dient der Selector-Teil in einem FAR-Pointer.

Zitat
sondern nur Segment-Override-Präfixe
Dafür gibt es nicht genügend Segment-Register, auf keiner CPU. Ein Task benötigt eine ganze Reihe an Segmenten. Schau die mal die Menge an Sektionen in einem typischen Executable-File an.


Zitat
Für beide Modelle muss gelten das der schreibende Zugriff auf die Deskriptoren eine Exception wirft
Auch bei Lese-Zugriffen sollte eine Exception kommen, sonst könnte ja ein Programm die Internas von anderen Programmen, oder gar dem OS, ausspionieren.


Zitat
Und was hindert uns als OS-Designer daran beide Modelle im fertigen OS anzubieten.
Auf einem vorhandenen Flat-Memory-OS ein segmentiertes Programm laufen zu lassen stelle ich mir quasi unmöglich vor. Umgedreht sollte es gehen auch wenn es einiges an Arbeit kostet und natürlich alle Vorteile der Segmentierung wegfallen, wenn das OS dann noch das Paging ausschaltet (weil nicht benötigt) dann sind alle Schutzmechanismen weg und das Flat-Memory-Task läuft noch unsicherer als auf dem primitivsten Flat-Memory-OS.
Wenn Du wirklich ein OS für beide Memory-Modelle entwickeln möchtest dann nur zu, ich würde mich über Source-Code (natürlich nur zum inspirieren lassen) sehr freuen.


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

 

Einloggen