现在位置: 首页 > 汇编语言 > 正文

汇编语言 - 内存分段

内存分段(Memory Segmentation)是 x86 架构中的一个核心概念,它决定了程序在内存中如何组织代码、数据和栈空间。


为什么需要分段

在 x86 实模式下,CPU 使用 16 位寄存器,但需要访问 1MB 的内存空间(20 位地址)。

16 位寄存器只能表示 64KB 范围(2^16 = 65536),远远不够。

Intel 的解决方案是将内存划分为 段(Segment),每个段有基地址和偏移量,通过 段地址 + 偏移量 的方式组合出完整的物理地址。

物理地址计算公式:

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

虽然在保护模式(32 位)下,分段机制更多地用于内存保护和权限控制,但理解分段对于理解汇编程序结构仍然至关重要。


三种基本段

一个典型的汇编程序使用三个主要段:

段名称英文名用途对应的 Section
代码段Code Segment存放可执行的机器指令section .text
数据段Data Segment存放已初始化的全局变量section .data
栈段Stack Segment存放函数调用信息、局部变量操作系统自动管理

代码段(.text)

代码段存放程序的全部机器指令,是 只读、可执行 的内存区域。

操作系统在加载程序时,将代码段映射到只读内存页,防止程序意外修改指令。

实例

; 文件路径:code_segment.asm
; 演示代码段使用

section .text                   ; 开始代码段
    global _start

_start:
    mov eax, 1                  ; 这些指令都存放在代码段中
    mov ebx, 0
    int 0x80

; 以下是一个辅助函数,也存放在代码段
my_function:
    mov eax, 42
    ret

数据段(.data)

数据段存放程序中 已初始化的全局变量

数据段是可读可写的,程序运行时可以修改其中的内容。

实例

; 文件路径:data_segment.asm
; 演示数据段使用

section .data
    ; 定义各种类型的已初始化变量
    msg db 'Hello, runoob!', 0     ; 字符串变量(字节序列)
    count db 100                    ; 字节变量,初始值 100
    pi dd 314159                    ; 双字变量,存放 π 的近似值 × 100000
    array db 1, 2, 3, 4, 5         ; 字节数组

section .text
    global _start

_start:
    ; 读取数据段中的变量
    mov al, [count]                ; 将 count 的值(100)加载到 al 寄存器
    ; ...
    mov eax, 1
    mov ebx, 0
    int 0x80

BSS 段(.bss)

BSS(Block Started by Symbol)段存放 未初始化的全局变量

与数据段不同,BSS 段在可执行文件中不占用实际空间,只在程序加载时由操作系统分配内存并清零。

实例

; 文件路径:bss_segment.asm
; 演示 BSS 段使用

section .bss
    buffer resb 256                 ; 预留 256 字节的缓冲区(未初始化)
    num_array resd 100              ; 预留 100 个双字(400 字节)

section .data
    ; 已初始化数据

section .text
    global _start

_start:
    ; 使用 BSS 段的缓冲区
    mov byte [buffer], 'A'          ; 向缓冲区写入字符 'A'
    ; ...
    mov eax, 1
    mov ebx, 0
    int 0x80

将未初始化的数据放在 BSS 段而不是 .data 段中,可以减小可执行文件的体积。例如,一个 10KB 的未初始化缓冲区在 BSS 段中不占用文件大小,但如果放在 .data 段中则会让文件增大 10KB。


段寄存器

x86 架构有 6 个段寄存器,用于跟踪当前正在使用的段:

段寄存器英文全称用途
CSCode Segment指向代码段,存放当前执行指令所在的段基址
DSData Segment指向数据段,存放大部分数据访问的段基址
SSStack Segment指向栈段,存放栈所在的段基址
ESExtra Segment附加段寄存器,用于字符串操作等额外数据段
FSGeneral Purpose通用段寄存器,常用于线程局部存储
GSGeneral Purpose通用段寄存器,常用于线程局部存储

在 32 位保护模式下,程序员通常不需要手动设置段寄存器,操作系统和链接器会处理好。


内存分段示意图

一个运行中的程序在内存中的分布如下:

程序内存布局图

一个运行中的程序在内存中的分布大致如下:

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

实模式 vs 保护模式

特性实模式(16 位)保护模式(32 位)
内存寻址段地址 × 16 + 偏移段选择子查描述符表 + 偏移
最大内存1MB4GB(32 位)
内存保护无保护有页级和段级保护
多任务不支持支持
寻址方式segment:offsetselector:offset(通过描述符表)

本教程主要介绍 32 位保护模式下的汇编编程。在这种模式下,段的概念更多地是对内存区域的逻辑划分,物理地址由分页机制管理。你可以把 section .data/.bss/.text 简单地理解为程序不同部分在内存中的标记。