--- ingested: true ingestedAt: 2026-05-17 --- 标题: 汇编语言 - 内存分段 链接: https://www.runoob.com/assembly/assembly-memory-segment.html 内容: ## 汇编语言 - 内存分段 内存分段(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 个段寄存器,用于跟踪当前正在使用的段: | 段寄存器 | 英文全称 | 用途 | | ---- | --------------- | -------------------- | | CS | Code Segment | 指向代码段,存放当前执行指令所在的段基址 | | DS | Data Segment | 指向数据段,存放大部分数据访问的段基址 | | SS | Stack Segment | 指向栈段,存放栈所在的段基址 | | ES | Extra Segment | 附加段寄存器,用于字符串操作等额外数据段 | | FS | General Purpose | 通用段寄存器,常用于线程局部存储 | | GS | General Purpose | 通用段寄存器,常用于线程局部存储 | 在 32 位保护模式下,程序员通常不需要手动设置段寄存器,操作系统和链接器会处理好。 --- ## 内存分段示意图 一个运行中的程序在内存中的分布如下:  一个运行中的程序在内存中的分布大致如下: 高地址 +-------------------+ | 栈 (Stack) | <-- SS:ESP 指向栈顶 | 向下增长 | +-------------------+ | | | 空闲内存 | | | +-------------------+ | BSS 段 (.bss) | 未初始化的全局变量 +-------------------+ | 数据段 (.data) | 已初始化的全局变量 +-------------------+ | 代码段 (.text) | 程序指令(只读) +-------------------+ | 保留区域 | 操作系统使用 +-------------------+ 低地址 --- ## 实模式 vs 保护模式 | 特性 | 实模式(16 位) | 保护模式(32 位) | | ---- | -------------- | ----------------------- | | 内存寻址 | 段地址 × 16 + 偏移 | 段选择子查描述符表 + 偏移 | | 最大内存 | 1MB | 4GB(32 位) | | 内存保护 | 无保护 | 有页级和段级保护 | | 多任务 | 不支持 | 支持 | | 寻址方式 | segment:offset | selector:offset(通过描述符表) | > 本教程主要介绍 32 位保护模式下的汇编编程。在这种模式下,段的概念更多地是对内存区域的逻辑划分,物理地址由分页机制管理。你可以把 section .data/.bss/.text 简单地理解为程序不同部分在内存中的标记。