Welche Speicheradressräume gibt es?

Lesezeit: 9 Minuten

Benutzer-Avatar
Eric Postpischil

Welche Formen von Speicheradressräumen wurden verwendet?

Heutzutage ist ein großer flacher virtueller Adressraum üblich. In der Vergangenheit wurden kompliziertere Adressräume verwendet, wie z. B. ein Paar aus einer Basisadresse und einem Offset, ein Paar aus einer Segmentnummer und einem Offset, eine Wortadresse plus einen Index für ein Byte oder ein anderes Unterobjekt und so weiter .

Von Zeit zu Zeit wird in verschiedenen Antworten und Kommentaren behauptet, dass Zeiger in C (oder C++) im Wesentlichen Ganzzahlen sind. Das ist ein falsches Modell für C (oder C++), da die Vielfalt der Adressräume zweifellos die Ursache für einige der C- (oder C++-) Regeln für Zeigeroperationen ist. Wenn Sie beispielsweise keine Zeigerarithmetik über ein Array hinaus definieren, wird die Unterstützung für Zeiger in einem Basis- und Offsetmodell vereinfacht. Beschränkungen bei der Zeigerkonvertierung vereinfachen die Unterstützung für Adress-plus-Zusatzdaten-Modelle.

Diese wiederkehrende Behauptung motiviert diese Frage. Ich suche nach Informationen über die Vielzahl von Adressräumen, um zu veranschaulichen, dass ein C-Zeiger nicht unbedingt eine einfache Ganzzahl ist und dass die C-Einschränkungen für Zeigeroperationen angesichts der Vielzahl von zu unterstützenden Maschinen sinnvoll sind.

Nützliche Informationen können sein:

  • Beispiele für Computerarchitekturen mit verschiedenen Adressräumen und Beschreibungen dieser Räume.
  • Beispiele für verschiedene Adressräume, die noch in Maschinen verwendet werden, die derzeit hergestellt werden.
  • Verweise auf Dokumentation oder Erklärung, insbesondere URLs.
  • Ausarbeitung, wie Adressräume C-Zeigerregeln motivieren.

Dies ist eine weit gefasste Frage, daher bin ich offen für Vorschläge zur Handhabung. Ich würde mich über eine gemeinsame Bearbeitung einer einzigen allgemein umfassenden Antwort freuen. Dies kann jedoch dazu führen, dass der verdiente Ruf nicht verliehen wird. Ich schlage vor, mehrere nützliche Beiträge hochzustimmen.

  • Also machst du im Grunde deine Hausaufgaben für dich?

    – Markus B

    30. Dezember 2012 um 15:16 Uhr

  • Dies ist kein Diskussionsforum.

    – Markus B

    30. Dezember 2012 um 15:19 Uhr

  • Die Frage ist eine praktische, beantwortbare Frage, die für den Programmiererberuf einzigartig ist. Offensichtlich gibt es spezifische unterschiedliche Speicheradressräume. Dies ist keine Ansichtssache. Und ihre Eigenschaften müssen verstanden werden, um zu verstehen, warum C/C++-Regeln so sind, wie sie sind. Natürlich mussten die Sprachkomitees diese Informationen haben, um die Standards zu erstellen.

    – Eric Postpischil

    30. Dezember 2012 um 15:25 Uhr

  • c-faq.com/null/machexamp.html

    – Melpomen

    30. Dezember 2012 um 15:31 Uhr

  • @netcoder: Wikipedia könnte ein guter Ort sein, um sie aufzulisten, aber es ist kein guter Ort, um die Liste zu sammeln. Ich versuche Informationen zu sammeln, daher eine Frage.

    – Eric Postpischil

    30. Dezember 2012 um 15:31 Uhr

Benutzer-Avatar
James Kanze

Fast alles, was Sie sich vorstellen können, wurde wahrscheinlich verwendet. Die erste große Unterteilung besteht zwischen der Byte-Adressierung (alle modernen Architekturen) und der Wort-Adressierung (vor IBM 360/PDP-11, aber ich denke, dass moderne Unisys-Mainframes immer noch wortadressiert sind). Bei der Wortadressierung char* und void* wäre oft größer als ein int*; Selbst wenn sie nicht größer wären, würde der “Byte-Selektor” in den höherwertigen Bits liegen, die 0 sein müssen, oder für alles andere als Bytes ignoriert werden. (Auf einem PDP-10 zum Beispiel, wenn p war ein char*, (int)p < (int)(p+1) wäre aber oft falsch int und char* hatte die gleiche Größe.)

Unter den Byte-adressierten Maschinen sind die Hauptvarianten segmentierte und nicht-segmentierte Architekturen. Beide sind heute noch weit verbreitet, obwohl im Fall von Intel 32bit (einer segmentierten Architektur mit 48-Bit-Adressen) einige der weiter verbreiteten Betriebssysteme (Windows und Linux) Benutzerprozesse künstlich auf ein einzelnes Segment beschränken und eine flache Adressierung simulieren.

Obwohl ich keine neueren Erfahrungen habe, würde ich noch mehr Vielfalt bei eingebetteten Prozessoren erwarten. Insbesondere verwendeten eingebettete Prozessoren in der Vergangenheit häufig eine Harvard-Architektur, in der sich Code und Daten in unabhängigen Adressräumen befanden (damit ein Funktionszeiger und ein Datenzeiger, die in einen ausreichend großen ganzzahligen Typ umgewandelt wurden, gleich verglichen werden konnten ).

Benutzer-Avatar
Nemo

Ich würde sagen, Sie stellen die falsche Frage, außer aus historischer Neugier.

Selbst wenn Ihr System zufällig einen flachen Adressraum verwendet – selbst wenn jedes System von jetzt an bis zum Ende der Zeit einen flachen Adressraum verwendet – können Sie Zeiger immer noch nicht als ganze Zahlen behandeln.

Die C- und C++-Standards lassen alle Arten von Zeigerarithmetik “undefiniert”. Das kann sich jetzt auf jedem System auf Sie auswirken, da Compiler davon ausgehen, dass Sie undefiniertes Verhalten vermeiden und entsprechend optimieren.

Als konkretes Beispiel ist vor drei Monaten ein sehr interessanter Fehler in Valgrind aufgetaucht:

https://sourceforge.net/p/valgrind/mailman/message/29730736/

(Klicken Sie auf „Gesamten Thread anzeigen“ und suchen Sie dann nach „undefiniertes Verhalten“.)

Im Grunde verwendete Valgrind Kleiner-als- und Größer-als-Zeiger, um festzustellen, ob eine automatische Variable innerhalb eines bestimmten Bereichs lag. Da Vergleiche zwischen Zeigern in verschiedenen Aggregaten “undefiniert” sind, wurde Clang einfach wegoptimiert alle der Vergleiche, um eine Konstante wahr (oder falsch; ich habe es vergessen) zurückzugeben.

Dieser Fehler selbst hat eine interessante StackOverflow-Frage hervorgebracht.

Während also die ursprünglichen Zeigerarithmetik-Definitionen für echte Maschinen gedacht waren und das für sich genommen interessant sein könnte, ist es für die heutige Programmierung eigentlich irrelevant. Was heute relevant ist, ist, dass Sie einfach nicht davon ausgehen können, dass sich Zeiger wie ganze Zahlen verhalten, Punkt, unabhängig davon, welches System Sie gerade verwenden. „Undefiniertes Verhalten“ bedeutet nicht „etwas Komisches passiert“; Das bedeutet, dass der Compiler davon ausgehen kann, dass Sie sich nicht daran beteiligen. Wenn Sie dies tun, führen Sie einen Widerspruch in die Argumentation des Compilers ein; und aus einem Widerspruch folgt alles … Es hängt nur davon ab, wie schlau Ihr Compiler ist.

Und sie werden immer klüger.

  • @EricPostpischil: Vorerst behoben

    – Nemo

    24. Juni 2019 um 21:26 Uhr

  • Implementierungen können die Sprache frei erweitern, indem sie nützliche Aspekte des Zeigerverhaltens über die vom Standard vorgeschriebenen hinaus spezifizieren, damit Programme Dinge erreichen können, die sonst nicht möglich wären. Der Standard unternimmt keinen Versuch, alle Verhaltensweisen zu definieren, die zum Erfüllen einer bestimmten Aufgabe erforderlich sind, sondern stützt sich stattdessen auf Implementierungen, um alle “beliebten Erweiterungen” zu unterstützen, die möglicherweise erforderlich sind, um das zu tun, was getan werden muss.

    – Superkatze

    25. Juni 2019 um 0:32 Uhr

  • @supercat: Implementierungen können jedes gewünschte Verhalten definieren. Wenn Sie für eine bestimmte Implementierung codieren, ist das in Ordnung. Aber wenn Sie in C++ programmieren, müssen Sie undefiniertes Verhalten vermeiden, Punkt. Ich habe sogar ein Beispiel aus der Praxis gegeben, bei dem Clang undefinierte Zeigervergleiche in eine No-Op optimiert und einen Fehler in Valgrind aufgedeckt hat, obwohl das System einen flachen Adressraum hat. Ihr Kommentar ist also weder für diese Frage noch für meine Antwort relevant.

    – Nemo

    25. Juni 2019 um 0:37 Uhr


  • Wenn man Low-Level-Programmierung durchführen möchte, muss man eine Implementierung verwenden, die so entworfen oder konfiguriert ist, dass sie für diesen Zweck geeignet ist. Wenn Optimierungen aktiviert sind, schätzen die Autoren von clang und gcc die Leistung von Code, der nichts erfordert, was nicht vom Standard vorgeschrieben ist, über die Fähigkeit, alles zu tun, was nicht vom Standard vorgesehen ist, aber das bedeutet einfach, dass das Aktivieren von Optimierungen sie ungeeignet macht für einige Zwecke.

    – Superkatze

    25. Juni 2019 um 0:59 Uhr


Es gibt verschiedene Formen von bankgeschaltetem Speicher.

Ich arbeitete an einem eingebetteten System mit 128 KB Gesamtspeicher: 64 KB RAM und 64 KB EPROM. Zeiger waren nur 16-Bit, sodass ein Zeiger in den RAM den gleichen Wert wie ein Zeiger im EPROM haben konnte, obwohl sie sich auf unterschiedliche Speicherorte bezogen.

Der Compiler verfolgte den Typ des Zeigers, damit er die Anweisung(en) erzeugen konnte, um die richtige Bank auszuwählen, bevor ein Zeiger dereferenziert wurde.

Man könnte argumentieren, dass dies wie Segment + Offset war, und auf Hardwareebene war es im Wesentlichen so. Aber das Segment (oder genauer gesagt die Bank) war implizit aus dem Typ des Zeigers und wurde nicht als Wert eines Zeigers gespeichert. Wenn Sie einen Zeiger im Debugger untersucht haben, sehen Sie nur einen 16-Bit-Wert. Um zu wissen, ob es ein Offset ins RAM oder ins ROM war, musste man den Typ kennen.

Zum Beispiel, Foo * konnte nur im RAM sein und const Bar * kann nur im ROM sein. Wenn Sie a kopieren müssten Bar in RAM, wäre die Kopie tatsächlich ein anderer Typ. (Es war nicht so einfach wie const/non-const: Alles im ROM war const, aber nicht alle consts waren im ROM.)

Das war alles in C, und ich weiß, dass wir nicht standardmäßige Erweiterungen verwendet haben, damit das funktioniert. Ich vermute, dass ein 100% kompatibler C-Compiler damit wahrscheinlich nicht fertig werden könnte.

Aus der Sicht eines C-Programmierers gibt es drei Hauptarten der Implementierung, um die man sich Sorgen machen muss:

  1. Diejenigen, die auf Maschinen mit einem linearen Speichermodell abzielen und die so konzipiert und/oder konfiguriert sind, dass sie als “High-Level-Assembler” verwendet werden können —etwas, das die Autoren des Standards ausdrücklich nicht ausschließen wollten. Die meisten Implementierungen verhalten sich auf diese Weise, wenn Optimierungen deaktiviert sind.

  2. Solche, die als “High-Level-Assembler” für Maschinen mit ungewöhnlichen Speicherarchitekturen verwendbar sind.

  3. Diejenigen, deren Design und/oder Konfiguration sie nur für Aufgaben geeignet machen, die keine Low-Level-Programmierung beinhalten, einschließlich clang und gcc, wenn Optimierungen aktiviert sind.

Speicherverwaltungscode, der auf den ersten Implementierungstyp abzielt, ist häufig mit allen Implementierungen dieses Typs kompatibel, deren Ziele dieselben Darstellungen für Zeiger und ganze Zahlen verwenden. Der Speicherverwaltungscode für den zweiten Implementierungstyp muss häufig speziell auf die jeweilige Hardwarearchitektur zugeschnitten werden. Plattformen, die keine lineare Adressierung verwenden, sind ausreichend selten und ausreichend vielfältig, dass es nicht erforderlich ist, Code für ein bestimmtes Stück ungewöhnlicher Hardware zu schreiben oder zu warten (z. B. weil es ein teures Stück Industrieausrüstung antreibt, für das modernere Controller nicht verfügbar) ist die Kenntnis einer bestimmten Architektur wahrscheinlich nicht von großem Nutzen.

Implementierungen des dritten Typs sollten nur für Programme verwendet werden, die keine Speicherverwaltungs- oder Systemprogrammierungsaufgaben ausführen müssen. Da der Standard nicht verlangt, dass alle Implementierungen in der Lage sind, solche Aufgaben zu unterstützen, unternehmen einige Compiler-Autoren – selbst wenn sie auf Maschinen mit linearer Adresse abzielen – keinen Versuch, irgendeine der nützlichen Semantiken davon zu unterstützen. Selbst einige Prinzipien wie “ein Gleichheitsvergleich zwischen zwei gültigen Zeigern wird – im schlimmsten Fall – entweder 0 oder 1 ergeben, die auf möglicherweise nicht spezifizierte Weise gewählt werden, gelten nicht für solche Implementierungen.

1235800cookie-checkWelche Speicheradressräume gibt es?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy