南投县网站建设_网站建设公司_测试工程师_seo优化
2025/12/22 0:50:04 网站建设 项目流程

进程的地址空间是结构化的,对应的,我们将其划分为不同的段:代码段、数据段、堆段、栈段等,每个段之间相互独立互不影响。堆段和栈段的增长不会导致其他段被覆盖。每个段都有自己的地址,都可以从 0 开始。

相比分页,分段更适合做共享库。在分段下,可以直接映射一整个代码段,而在分页下,需要组合页面来完成“代码段”的映射。

3.7.1 Implementation of Pure Segmentation 纯粹分段的实现

只对内存进行分段,不做分页。

分页是按照固定的大小分割内存,分段则是按照结构来划分,每个段的大小都不一致。那么在只有分段没有分页的内存中,换出与换入段的大小不一致,容易导致大量内存碎片的产生,称为外部碎片(external fragmentation)或者棋盘形碎片(checkerboard)

这些内存碎片可以通过内存紧缩来回收,通过调整每个段的位置,消除段之间的空洞,当然这会消耗性能。

3.7.2 Segmentation with Paging: MULTICS 分段与分页的结合:MULTICS

结合了分段和分页的虚拟内存组织形式称为段页式,段页式结合了分段和分页的优点

  • 分页:统一的页面大小,只用加载需要的页面
  • 分段:易于编程、模块化、保护、共享

multics 每个段的虚拟地址空间是 36 位,并且每个段作为一个独立的虚拟地址空间进行分页,每个段都有一个段表,结构如下:

字段 长度(bit) 作用
页表描述符 18 指向该段的地址
段长度 9 说明段的长度,以页为单位
页面大小 1 0:1024字,1:64字
是否分页 1 0:分页,1:不分页
- 1 -
其他 3 -
保护位 3 -

1

虚拟地址分为段号、页号和页内偏移。寻址时:

  1. 根据段号找到对应的段描述符号
  2. 检查段的页面是否在内存中,如果不在就产生一个段错误,如果在就找到这个段。如果违反了段的访问保护限制,就发出一个越界错误
  3. 根据页号寻找页面,如果页面不在内存中则产生一个缺页中断
  4. 根据业内偏移量找到需要访问的地址

3.7.3 Segmentation with Paging: The Intel x86 分段与分页的结合:The Intel x86

相比 MULTICS,x86 的段更少但是容量更大。几乎没有进程会超过 1000 个段,但是有很多程序需要大段。

  • MULITCS: 256K 个独立的段,每个段最长可以容纳 64K 个 36 位字
  • x86: 16K 个独立的段,每个段最多可以容纳 10 亿个 32 位字

从 x86_64 开始,不再支持分段模式,但是依然保留了分段模式的机制。

在 x86 中,虚拟内存的核心是 局部描述符表LDT(Local Descriptor Table)全局描述符表GDT(Global Descriptor Table)。每个程序都有自己的 LDT,所有程序共享一个 GDT。

  • LDT: 描述每个程序的段,包括代码段、数据段、堆栈等。
  • GDT: 描述系统段,包括操作系统本身。

为了访问一个段,x86 需要将段的 selector 装入段寄存器中,不同的段对应不同的段寄存器,代码段对应 CS,数据段对应 DS。

  1. 先根据标志位选择 LDT 或者 GDT
  2. 选择将 selector 加载到内部的寄存器中,将低三位清零
  3. 将 LDT 或者 GDT 的地址加到寄存中,就得到了对应描述符的地址。

2

字段 长度(bit) 作用
index 13 对应段描述符的地址
- 1 0表述GDT 1表示LDT
- 2 保护位

段描述符大小 8 字节,结构如下:
3

字段 长度 作用
Base 32 段的基地址
Limit 20 段的长度
G 1 0:Limit 以字节为单位,1:Limit 以页为单位

注意:为 0 的段描述符是禁用的,用于表示段寄存器不可用。如果从寄存器中拿到了一个为 0 的段描述符,就触发一次 trap。

拿到段描述符后:

  1. 根据 Limit 检查偏移量是否在段内。如果不在段内,触发一次 trap

    如果 G 为 0,那么 Limit 以字节作为单位,最大为 1MB。当 G 为 1 时,Limit 以页面作为单位。

  2. 基地址 + 偏移量 = 线性地址

    如果禁止分页,那么线性地址就作为物理地址,从而可以得到一个纯分段的虚拟内存。

    如果允许分页,那么线性地址就作为虚拟地址,通过页表映射到物理地址。

在 32 位虚拟内存和 4 KB 页下,一个段可能包含百万数量的页面,因此使用两级映射减少页表的大小。

每个进程都有一个由 1024 个 32 位表项组成的页目录(page directory),通过一个全局寄存器定位。每个目录的项都指向一个包含 1024 个 32 位表项的页表,页表项指向页帧。

4

对应的,线性地址被划分为三部分:页目录、页面、页偏移量。

  • 页目录:作为索引,在页目录表中定位页表
  • 页面:作为索引,在页表中定位页面的位置
  • 页偏移量:用于寻找需要访问的物理地址

一个页表包含 1024 个页面,可以管理 4MB 内存,对于小于 4MB 的段,页目录中只有一个表项指向一个唯一的页表。这种方式较短的段的开销只有两个页面。

为了避免重复的内存访问,x86 使用 TLB 缓存近期使用过的页面映射。

某些应用程序不需要分段,需要一个独立的分页的 32 位地址空间。可以将所有的段寄存器设置为同一个 selector,将段描述符的基地址设置为 0, Limit 设置到允许的最大。

事实上,目前所有的 x86 系统都是这样的,只使用一个段,本质上是存粹的分页虚拟内存系统。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询