Assemblersprache - Speichersegmentierung

Die Speichersegmentierung ist ein Kernkonzept in der x86-Architektur, das bestimmt, wie ein Programm Code, Daten und Stapelspeicher im Speicher organisiert.


Warum Segmentierung erforderlich ist

Im x86-Realfahrmodus (Real Mode) verwendet die CPU 16-Bit-Register, muss jedoch auf 1 MB Speicherplatz zugreifen (20-Bit-Adressen).

Ein 16-Bit-Register kann nur einen Bereich von 64 KB darstellen (2^16 = 65536), was bei Weitem nicht ausreicht.

Die Lösung von Intel besteht darin, den Speicher inSegmente (Segment)zu unterteilen, wobei jedes Segment eine Basisadresse und einen Offset hat und durchSegmentadresse + Offsetdie vollständige physikalische Adresse gebildet wird.

Formel zur Berechnung der physikalischen Adresse:

物理地址 = 段地址 × 16 + 偏移地址

Obwohl im geschützten Modus (Protected Mode, 32-Bit) der Segmentierungsmechanismus eher für den Speicherschutz und die Zugriffskontrolle verwendet wird, ist das Verständnis der Segmentierung nach wie vor entscheidend, um die Struktur von Assemblerprogrammen zu verstehen.


Drei grundlegende Segmente

Ein typisches Assemblerprogramm verwendet drei Hauptsegmente:

SegmentnameEnglischer NameZweckEntsprechender Abschnitt
Code-SegmentCode-SegmentSpeichert ausführbare Maschinenbefehlesection .text
Daten-SegmentDaten-SegmentSpeichert initialisierte globale Variablensection .data
Stack-SegmentStack-SegmentSpeichert Funktionsaufrufinformationen und lokale VariablenWird automatisch vom Betriebssystem verwaltet

Code Segment (.text)

Das Code-Segment speichert alle Maschinenbefehle des Programms und ist einschreibgeschützter, ausführbarerSpeicherbereich.

Beim Laden eines Programms ordnet das Betriebssystem das Code-Segment schreibgeschützten Speicherseiten zu, um zu verhindern, dass das Programm Anweisungen versehentlich ändert.

Beispiel

; Dateipfad: code_segment.asm
; Demonstriert die Verwendung des Code-Segments

section .text                   ; Start des Code-Segments
    global _start

_start:
    mov eax, 1                  ; Diese Anweisungen sind alle im Code-Segment gespeichert
    mov ebx, 0
    int 0x80

; Das Folgende ist eine Hilfsfunktion, die ebenfalls im Code-Segment gespeichert ist
my_function:
    mov eax, 42
    ret

Data Segment (.data)

Das Daten-Segment speichertdie initialisierten globalen Variablen des Programms。

Das Daten-Segment ist lesbar und schreibbar, und sein Inhalt kann während der Programmausführung geändert werden.

Beispiel

; Dateipfad: data_segment.asm
; Demonstriert die Verwendung des Daten-Segments

section .data
    ; Definieren initialisierter Variablen verschiedener Typen
    msg db 'Hello, example!', 0     ; String-Variable (Bytsequenz)
    count db 100                    ; Byte-Variable, Anfangswert 100
    pi dd 314159                    ; Doppelwort-Variable, speichert die Annäherung von π × 100000
    array db 1, 2, 3, 4, 5         ; Byte-Array

section .text
    global _start

_start:
    ; Variablen aus dem Daten-Segment lesen
    mov al, [count]                ; Lädt den Wert von count (100) in das Register al
    ; ...
    mov eax, 1
    mov ebx, 0
    int 0x80

BSS Segment (.bss)

Das BSS-Segment (Block Started by Symbol) speichertuninitialisierte globale Variablen。

Im Gegensatz zum Daten-Segment beansprucht das BSS-Segment keinen tatsächlichen Platz in der ausführbaren Datei; Speicher wird dafür erst beim Laden des Programms durch das Betriebssystem zugewiesen und mit Nullen initialisiert.

Beispiel

; Dateipfad: bss_segment.asm
; Demonstriert die Verwendung des BSS-Segments

section .bss
    buffer resb 256                 ; Reserviert einen 256-Byte-Puffer (uninitialisiert)
    num_array resd 100              ; Reserviert 100 Doppelwörter (400 Bytes)

section .data
    ; Initialisierte Daten

section .text
    global _start

_start:
    ; Den Puffer im BSS-Segment verwenden
    mov byte [buffer], 'A'          ; Schreibt das Zeichen 'A' in den Puffer
    ; ...
    mov eax, 1
    mov ebx, 0
    int 0x80

Das Platzieren uninitialisierter Daten im BSS-Segment anstelle des .data-Segments kann die Größe der ausführbaren Datei verringern. Beispielsweise beansprucht ein 10 KB großer uninitialisierter Puffer im BSS-Segment keine Dateigröße, würde er jedoch im .data-Segment platziert, würde er die Datei um 10 KB vergrößern.


Segmentregister

Die x86-Architektur verfügt über 6 Segmentregister, die verwendet werden, um die aktuell verwendeten Segmente zu verfolgen:

SegmentregisterEnglischer vollständiger NameZweck
CSCode-SegmentZeigt auf das Code-Segment und speichert die Segmentbasisadresse, in der sich die aktuell auszuführenden Anweisungen befinden
DSDaten-SegmentZeigt auf das Daten-Segment und speichert die Segmentbasisadresse für die meisten Datenzugriffe
SSStack-SegmentZeigt auf das Stack-Segment und speichert die Segmentbasisadresse, in der sich der Stack befindet
ESExtra SegmentZusätzliches Segmentregister, das für zusätzliche Datensegmente wie String-Operationen verwendet wird
FSGeneral PurposeUniverselles Segmentregister, das häufig für Thread-lokalen Speicher verwendet wird
GSGeneral PurposeUniverselles Segmentregister, das häufig für Thread-lokalen Speicher verwendet wird

Im 32-Bit-geschützten Modus müssen Programmierer Segmentregister normalerweise nicht manuell einstellen; das Betriebssystem und der Linker kümmern sich darum.


Diagramm der Speichersegmentierung

Das Speicherlayout eines laufenden Programms sieht wie folgt aus:

程序内存布局图

Das ungefähre Speicherlayout eines laufenden Programms sieht wie folgt aus:

高地址
+-------------------+
|   栈 (Stack)      |  <-- SS:ESP 指向栈顶
|   向下增长         |
+-------------------+
|                   |
|   空闲内存         |
|                   |
+-------------------+
|   BSS 段 (.bss)   |  未初始化的全局变量
+-------------------+
|   数据段 (.data)   |  已初始化的全局变量
+-------------------+
|   代码段 (.text)   |  程序指令(只读)
+-------------------+
|   保留区域         |  操作系统使用
+-------------------+
低地址

Real Mode vs Protected Mode

MerkmalReal-Modus (16-Bit)Geschützter Modus (32-Bit)
SpeicheradressierungSegmentadresse × 16 + OffsetSegment-Selektor schlägt in Descriptortabelle nach + Offset
Maximaler Speicher1 MB4 GB (32-Bit)
SpeicherschutzKein SchutzHat Schutz auf Seiten- und Segmentebene
MultitaskingNicht unterstütztUnterstützt
Adressierungsmodussegment:offsetselector:offset (über Descriptortabelle)

Dieses Tutorial führt hauptsächlich in die Assemblerprogrammierung im 32-Bit-geschützten Modus ein. In diesem Modus ist das Konzept der Segmente eher eine logische Aufteilung von Speicherbereichen, wobei physische Adressen vom Paging-Mechanismus verwaltet werden. Sie können die Abschnitte .data/.bss/.text einfach als Markierungen für verschiedene Teile des Programms im Speicher verstehen.

Weitere Erweiterungen