内存的分页分段介绍
内存管理是操作系统核心功能之一,分页 (Paging) 和分段 (Segmentation) 是两种重要的内存管理技术,用于实现虚拟内存,解决物理内存大小限制、内存碎片、程序重定位和内存保护等问题。它们可以将程序的逻辑地址空间映射到物理内存地址空间。
一、 分段 (Segmentation)
-
基本思想: 分段是将程序的地址空间划分为若干个逻辑上独立的段 (Segment)。每个段都有一个名字(或编号)和长度,并且通常对应程序的一个逻辑部分,如代码段 (Code Segment)、数据段 (Data Segment)、栈段 (Stack Segment)、堆段 (Heap Segment) 等。程序员或编译器可以根据程序的逻辑结构来定义这些段。
-
地址结构: 逻辑地址由两部分组成:段号 (Segment Number) 和段内偏移量 (Offset within Segment)。
逻辑地址 = (段号, 段内偏移)
-
地址转换:
- 段表 (Segment Table):操作系统为每个进程维护一个段表。段表中的每个条目(段表项)对应一个段,记录了该段的基地址 (Base Address,即段在物理内存中的起始地址) 和界限 (Limit,即段的长度)。
- 转换过程:
- CPU根据逻辑地址中的段号查找段表。
- 检查段内偏移量是否小于段表项中记录的该段的界限(长度)。如果偏移量超界,则产生地址越界中断(保护错误)。
- 如果未越界,则物理地址 = 段基地址 + 段内偏移量。
-
优点:
- 逻辑相关性:分段更好地匹配程序的逻辑结构,便于信息的共享和保护。例如,可以将代码段设置为只读,数据段设置为可读写。不同的进程可以共享同一个代码段(如共享库)。
- 动态链接与增长:段可以动态增长(如堆和栈),并且在运行时动态链接不同的段。
- 易于编程:程序员可以从逻辑上组织程序,而不必关心物理内存的连续性。
-
缺点:
- 内存碎片:容易产生外部碎片。当段被换入换出或动态分配释放时,物理内存中可能会出现很多不连续的小空闲块,这些空闲块可能单独无法满足新段的需求,即使它们的总和足够。
- 段大小不一:段的大小可变且不固定,给内存分配和管理带来复杂性。
二、 分页 (Paging)
-
基本思想: 分页是将进程的逻辑地址空间和物理内存空间都划分为大小固定、连续的块。逻辑地址空间中的块称为页 (Page),物理内存中的块称为页帧 (Page Frame) 或物理块。页和页帧的大小通常是相同的(例如,4KB)。
-
地址结构: 逻辑地址由两部分组成:页号 (Page Number) 和页内偏移量 (Offset within Page)。
逻辑地址 = (页号, 页内偏移)
页号和页内偏移量的位数由地址总线宽度和页面大小决定。例如,32位地址,4KB页面大小(2^12 B),则页内偏移量占12位,页号占 32-12 = 20位。 -
地址转换:
- 页表 (Page Table):操作系统为每个进程维护一个页表。页表中的每个条目(页表项)对应一个逻辑页,记录了该页映射到的物理页帧号。页表项中还可能包含其他信息,如存在位(表示该页是否在内存中)、访问位、修改位、保护位(读/写/执行权限)等。
- 转换过程:
- CPU根据逻辑地址中的页号查找页表(通常页表的基地址存放在一个专门的寄存器中,如CR3寄存器在x86中)。
- 从页表项中获取该页对应的物理页帧号。
- 如果页表项中的存在位为0(表示缺页),则产生缺页中断 (Page Fault),操作系统会处理这个中断(如从磁盘调入页面)。
- 物理地址 = (物理页帧号 * 页面大小) + 页内偏移量。 (实际上,通常是将物理页帧号左移相应的位数,然后与页内偏移量进行或运算得到物理地址)。
-
优点:
- 内存利用率高:由于页面大小固定,可以有效地减少外部碎片。任何空闲的页帧都可以分配给任何页。
- 分配管理简单:物理内存以页帧为单位进行管理,分配和回收都比较简单。
- 支持虚拟内存:很容易实现请求调页和页面置换算法,从而支持比物理内存更大的虚拟地址空间。
-
缺点:
- 页表开销:页表本身需要占用内存空间。对于地址空间很大的进程,页表可能非常大。为了解决这个问题,发展出了多级页表、反向页表等技术。
- 逻辑分离性差:分页是基于物理划分,而不是逻辑划分。一个逻辑上完整的程序模块(如一个函数或数据结构)可能会跨越多个页面。
- 内部碎片:如果一个段或程序模块的大小不是页面大小的整数倍,那么最后一个页面会有一部分空间被浪费,这称为内部碎片。但通常内部碎片比分段产生的外部碎片问题要小。
三、 段页式管理 (Segmentation with Paging)
现代操作系统(如x86架构下的Linux和Windows)通常结合了分段和分页的优点,采用段页式管理。
-
基本思想: 先将程序的逻辑地址空间按段划分,然后再将每个段划分为固定大小的页。
-
地址结构: 逻辑地址通常形式上还是
(段选择子, 段内偏移)
。但是这个段内偏移会被进一步解释为(页号, 页内偏移)
。 -
地址转换:
- 分段单元:首先,通过段选择子在段描述符表 (GDT/LDT) 中找到对应段的描述符,获取段的基地址和界限,并进行权限检查。在现代保护模式的x86系统中,分段机制主要用于内存保护和隔离,段基址通常设为0(扁平内存模型),段的界限设为最大,所以逻辑地址的段内偏移部分直接作为线性地址 (Linear Address)。
- 分页单元:然后,将这个线性地址(在扁平模型下,线性地址就等于逻辑地址的偏移部分)看作是
(页目录索引, 页表索引, 页内偏移)
(以二级页表为例)。- 使用线性地址的高位部分作为索引查找页目录表,得到页表的物理地址。
- 使用线性地址的中间部分作为索引查找该页表,得到物理页帧号。
- 最后,物理页帧号与页内偏移量组合形成最终的物理地址。
-
优点:
- 结合了分段和分页的优点:既有分段的逻辑划分、共享和保护能力,又有分页的内存利用率高、管理方便的特点。
- 在x86架构中,分段提供了硬件级别的保护机制,而分页则负责实现虚拟内存和细粒度的内存管理。
-
缺点:
- 系统复杂度增加:地址转换过程更长,需要查询段表和多级页表。
- 开销增加:需要更多的硬件支持(如MMU)和内存来存储段表和页表。
在现代主流的64位操作系统中,如Linux和Windows,通常采用“扁平内存模型 (Flat Memory Model)”,分段机制被弱化,主要用于设置特权级等,而主要的虚拟地址到物理地址的转换由多级分页机制完成。逻辑地址空间近似为一个巨大的、连续的线性地址空间,然后这个线性地址空间再通过分页映射到物理内存。
拓展延申:
-
TLB (Translation Lookaside Buffer): 由于每次访存都可能需要多次查表(段表、多级页表),为了加速地址转换过程,CPU内部通常集成了一个高速缓存,称为TLB。TLB缓存了最近使用过的虚拟页号到物理页帧号的映射关系。当进行地址转换时,首先在TLB中查找,如果命中 (TLB Hit),则直接获取物理页帧号,无需访问内存中的页表,速度极快。如果未命中 (TLB Miss),则需要访问内存中的页表,并将查到的映射关系存入TLB以备后续使用(可能会替换掉TLB中的某个旧条目)。
-
大页面 (Huge Pages / Large Pages): 为了减少TLB Miss和页表管理的开销,特别是在处理大块连续内存(如数据库缓冲区、虚拟机内存)时,现代CPU和操作系统支持大页面。例如,除了4KB的页面,还可以支持2MB或1GB的页面。使用大页面可以减少页表项的数量,提高TLB的覆盖范围。
-
页面置换算法 (Page Replacement Algorithms): 在虚拟内存系统中,当发生缺页中断且物理内存已满时,需要选择一个或多个已在内存中的页面换出到磁盘(如果它被修改过,则需要写回),以便为新调入的页面腾出空间。常见的页面置换算法有FIFO、LRU (Least Recently Used)、LFU (Least Frequently Used)、Clock (NRU - Not Recently Used) 算法及其变种。