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:
| Segmentname | Englischer Name | Zweck | Entsprechender Abschnitt |
|---|---|---|---|
| Code-Segment | Code-Segment | Speichert ausführbare Maschinenbefehle | section .text |
| Daten-Segment | Daten-Segment | Speichert initialisierte globale Variablen | section .data |
| Stack-Segment | Stack-Segment | Speichert Funktionsaufrufinformationen und lokale Variablen | Wird 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
; 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
; 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
; 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:
| Segmentregister | Englischer vollständiger Name | Zweck |
|---|---|---|
| CS | Code-Segment | Zeigt auf das Code-Segment und speichert die Segmentbasisadresse, in der sich die aktuell auszuführenden Anweisungen befinden |
| DS | Daten-Segment | Zeigt auf das Daten-Segment und speichert die Segmentbasisadresse für die meisten Datenzugriffe |
| SS | Stack-Segment | Zeigt auf das Stack-Segment und speichert die Segmentbasisadresse, in der sich der Stack befindet |
| ES | Extra Segment | Zusätzliches Segmentregister, das für zusätzliche Datensegmente wie String-Operationen verwendet wird |
| FS | General Purpose | Universelles Segmentregister, das häufig für Thread-lokalen Speicher verwendet wird |
| GS | General Purpose | Universelles 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
| Merkmal | Real-Modus (16-Bit) | Geschützter Modus (32-Bit) |
|---|---|---|
| Speicheradressierung | Segmentadresse × 16 + Offset | Segment-Selektor schlägt in Descriptortabelle nach + Offset |
| Maximaler Speicher | 1 MB | 4 GB (32-Bit) |
| Speicherschutz | Kein Schutz | Hat Schutz auf Seiten- und Segmentebene |
| Multitasking | Nicht unterstützt | Unterstützt |
| Adressierungsmodus | segment:offset | selector:offset (über Descriptortabelle) |
Weitere ErweiterungenDieses 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.