正文
1
段的概念
在计算机架构中,段的概念是用于组织和管理内存的一种方式,特别是在早期的 x86 架构中,什么8086处理器啥的,不过现在很多概念一直沿用。
每个段代表了内存中的一个逻辑区域,用于不同类型的数据或代码。这种分段机制可以提高程序的灵活性和安全性。
下面是一些常见的段类型:
1. 代码段 (Code Segment, CS)
代码段主要是存储程序的可执行指令。
该段只读,防止程序在运行时意外修改其自身的指令。包含所有执行的逻辑,通常以机器指令的形式存在。
2. 数据段 (Data Segment, DS)
数据段存储程序运行所需的全局变量和静态变量。
该段可读可写,允许程序在运行过程中修改数据。
初始化部分存储初始值,未初始化部分(BSS 段)通常会被默认设置为零。
3. 堆栈段 (Stack Segment, SS)
定义:堆栈段用于存储函数调用时的局部变量、返回地址以及其他临时数据。
该段采用后进先出(LIFO)的结构。
支持函数调用和返回,存储参数和局部变量。
当然我们分析elf文件的时候会发现还有很多类型的段:
.text
:包含可执行代码。.data
:包含已初始化的全局变量和静态变量。.bss
:包含未初始化的全局变量和静态变量,其大小在程序加载时会被设置为 0。.rodata
:包含只读数据,例如字符串常量。.symtab
:符号表,存储程序中的符号信息。.strtab
:字符串表,存储符号名称和其他字符串。.rel
或.rela
:重定位信息,用于链接器处理地址修正。
2
段带来的好处
内存保护:通过不同的段,可以对内存区域进行访问控制,防止程序错误地修改其他段的内容。
模块化:代码段和数据段的分离使得程序的结构更加清晰,有助于模块化设计和维护。
动态内存管理:分段允许程序在运行时动态地请求和释放内存,提高了内存利用率。
虽然在现代计算机架构中,分段机制可能不再广泛使用,但其基本思想仍然影响着内存管理的设计和实现。
3
段的特点
段内的地址通常被视为连续的。这意味着在一个特定的段(如代码段、数据段或堆栈段)内,地址是顺序排列的,从段的起始地址到段的结束地址。
连续性:
段内的地址是连续的,这使得访问和管理内存更为简单。例如,在数据段中,变量的地址会依次排列,便于程序在运行时进行访问。
逻辑划分:
虽然物理内存可能是不连续的,但逻辑上每个段的地址范围是连续的。操作系统通过内存管理单元(MMU)来处理虚拟地址与物理地址之间的映射。
段的大小:
每个段的大小可以不同,代码段可能比数据段大,或者相反。但在每个段内部,地址是线性且连续的。
分隔符:
段之间的地址通常不连续,段的开始和结束由段寄存器管理。例如,如果一个段的起始地址是 0x1000,而下一个段的起始地址是 0x2000,那么这两个段之间的地址并不连续。
4
段寻址示例
8086的分段寻址算是最早期、最简单的分段机制了,该分段寻址过程涉及段寄存器和偏移量的组合,即段寄存器内存左移+偏移量便得到了索要访问的地址。
那为什么需要将段寄存器的内容左移 4 位并与偏移量相加,很多朋友并不是很理解,我们可以从以下几个方面进行解析:
在 x86 架构中,段寄存器包含的是段的基地址,而这个基地址是以 16 字节为单位的。
偏移量的概念:偏移量是指在指定段内的具体位置,它是以字节为单位的。
物理地址的计算:
段寄存器的值左移 4 位(即乘以 16)是因为其内容实际上表示的是段的起始地址,以 16 为单位。例如,如果段寄存器中的值是 0x000A(十六进制),那么左移后得到的物理地址为 0x000A0。
将左移后的段基地址与偏移量相加,就可以得到实际的物理地址。
1MB 的内存限制:在 x86 体系结构中,20 位的物理地址可以表示最大 2^20 = 1MB 的内存空间。这是因为 20 位二进制数的范围是从 0 到 1,048,575(0x000000 到 0xFFFFF)。
所以将段寄存器的内容左移 4 位后加上偏移量,我们能够综合得到一个 20 位的物理地址,从而能够有效访问最多 1MB 的内存空间。虽然现代计算机系统中没有在选用这种方式,但核心思想并没有发生很大的变化。