目录
分页存储管理方式是一种 commonly used 的内存管理技术,它将内存和磁盘空间分为固定大小的块,称为页和块。分页存储管理的核心思想是将进程的地址空间分为多个页,并将这些页映射到内存中的物理页框。这篇博客将探讨分页存储管理的基本方法、地址变换机构、引入块表后的内存有效访问时间,以及两级页表、多级页表和反置页表等高级技术。
分页存储管理的基本方法
分页的基本思想是将进程的逻辑地址空间划分为固定大小的页,并将这些页映射到物理内存中的页框。每个页和页框都有一个唯一的标识,分别称为页号和页框号。
-
页(Page):
- 进程的逻辑地址空间划分为大小固定的页面。
- 每个页有一个唯一的页号(Page Number)。
-
页框(Page Frame):
- 物理内存同样划分为大小相同的页框。
- 每个页框有一个唯一的页框号(Frame Number)。
分页存储管理的主要优点
-
实现虚拟内存:
- 机制:通过地址映射,将进程的逻辑地址映射到物理内存地址,使得进程可以访问比实际物理内存更大的地址空间。
- 效果:进程无需一次性加载完整程序,只需按需加载所需页,实现内存扩展,优化资源利用。
-
提高内存利用率:
- 共享内存页:允许不同进程共享公共代码或数据页,减少冗余。
- 按需装入:在进程实际需要时加载相应页,不用一次性加载所有页,减少内存占用,实现高效利用。
-
实现共享和保护:
- 共享页表:通过页表共享和引用计数,可实现进程间内存页共享,例如共享库。
- 访问控制:在页表中设定访问权限,如读、写、执行等,实现进程间的内存保护,防止非法访问。
分页存储管理的主要步骤
-
地址空间的分页:
- 过程:将进程的逻辑地址空间划分为固定大小的页。一个逻辑地址由页号和页内偏移量两部分组成。
- 示例:假设页大小为4KB,则页号和页内偏移量的计算为:
- 页号 = 逻辑地址 / 页大小
- 页内偏移量 = 逻辑地址 % 页大小
-
页的装入和替换:
- 页的装入:根据需要,将页从外存(如硬盘)装入内存中的空闲页框。
- 页替换算法:采用算法(如LRU、FIFO、LFU等)决定将哪个不经常使用的页替换出去,以腾出空间装入新的页。
示例页替换算法: - LRU(Least Recently Used,最近最少使用):替换最近最久未使用的页。
- FIFO(First In First Out,先进先出):按时间顺序,最早装入的页最早被替换。
- LFU(Least Frequently Used,最少使用):替换使用频率最少的页。
-
地址变换机构(Address Translation Mechanism):
- 页表(Page Table):记录每个逻辑页号对应的物理页框号,页表存放在内存中。
- 转换机构:将逻辑地址转换为物理地址,通过从页表中查询页号对应的页框号并加上页内偏移量。
示例伪代码:
function addressTranslation(logicalAddress) {
pageIndex = logicalAddress / pageSize;
offset = logicalAddress % pageSize;
frameIndex = pageTable[pageIndex];
physicalAddress = frameIndex * pageSize + offset;
return physicalAddress;
}
内存访问过程
- 逻辑地址生成:进程生成逻辑地址,由页号和页偏移量组成。
- 页表查询:通过页表查找页号对应的页框号。
- 地址转换:将逻辑地址转换为物理地址,形成物理内存中的访问地址。
- 访问内存:根据转换后的物理地址访问物理内存中的数据。
分页存储管理的实际应用
现代操作系统广泛应用分页存储管理,实现高效内存管理和进程隔离:
- 多任务操作系统:分页技术使得多个运行的进程能够独立管理其地址空间,提高系统稳定性和安全性。
- 虚拟内存扩展:通过虚拟内存技术,将不活跃的页面交换至硬盘,实现内存扩展,提高整体系统的性能。
- 内存保护机制:通过页表设置访问权限,防止非法访问和进程间干扰。
地址变换机构
地址变换机构通过分页技术将虚拟地址转换为物理地址。分页技术将内存划分为固定大小的页(Page)和页框(Frame),虚拟地址由页号(Page Number)和页内偏移量(Offset)组成。
地址变换的过程
地址变换的过程可以分为以下几个步骤:
1. 取出虚拟地址
进程指令中的虚拟地址由页号和页内偏移量组成。假设虚拟地址为 VA
,页号为 P
,页内偏移量为 d
。
2. 计算页号
从虚拟地址中提取页号 P
。页号的位数取决于虚拟地址空间和页大小。
3. 页表变换
根据页号 P
访问页表,获取相应的页框号 F
。页表是一个映射表,存储每个页对应的物理内存页框号。
4. 计算偏移量
从虚拟地址中提取偏移量 d
。偏移量表示页内的具体地址。
5. 生成物理地址
将页框号 F
和偏移量 d
组合,生成物理地址 PA
。物理地址的计算公式如下:
地址变换的示例
以下是一个示例,展示了地址变换的具体过程:
假设:
- 页大小为 4KB(即 4096 字节)。
- 虚拟地址
VA
为 0x1234。 - 页号
P
为高 12 位,偏移量d
为低 12 位。
1. 取出虚拟地址
虚拟地址 VA
为 0x1234。
2. 计算页号
页号 P
为 0x0001(高 12 位)。
3. 页表变换
假设页表中,页号 0x0001 对应的页框号 F
为 0x002。
4. 计算偏移量
偏移量 d
为 0x034(低 12 位)。
5. 生成物理地址
物理地址 PA
为:
PA = (页框号 F) * (页大小) + 偏移量 d = 0x002 * 4096 + 0x034 = 0x0800 + 0x034 = 0x0834
解释
- 虚拟地址 VA 被分成两部分:页号 P 和偏移量 d。页号 P 用于确定页框号 F,偏移量 d 用于确定页框内的数据位置。
- 页表是用于将虚拟地址转换为物理地址的映射表。页表中,每个页表项包含一个页号和一个对应的页框号。
- 页框号 F 是一个物理地址,它指向一个物理页面的起始位置。
- 偏移量 d 是一个相对地址,它指示虚拟页面中的哪个字节需要访问。
- 最终的物理地址 PA 是页框号 F 和偏移量 d 的组合。
地址变换的硬件支持
地址变换通常由硬件实现,涉及以下几个关键组件:
1. 基址寄存器(Base Register)
基址寄存器存储页表的起始地址。
2. 界限寄存器(Limit Register)
界限寄存器存储页表的大小,用于检查虚拟地址是否越界。
3. 页表(Page Table)
页表是一个映射表,存储每个页对应的物理内存页框号。页表通常存储在内存中,但为了提高访问速度,常用的页表项可以缓存到快表(Translation Lookaside Buffer, TLB)中。
地址变换的实现示例
以下是一个简单的实现示例,展示了如何通过地址变换机构将虚拟地址转换为物理地址:
#include <stdio.h>
#define PAGE_SIZE 4096 // 页大小
typedef struct {
int frame_number;
int valid; // 页表项是否有效
} PageTableEntry;
PageTableEntry page_table[] = {
{0x001, 1}, // 页号 0
{0x002, 1}, // 页号 1
{0x003, 1}, // 页号 2
// 其他页表项
};
int translate_address(int virtual_address) {
int page_number = virtual_address / PAGE_SIZE; // 计算页号
int offset = virtual_address % PAGE_SIZE; // 计算偏移量
if (page_number >= sizeof(page_table) / sizeof(page_table[0]) || !page_table[page_number].valid) {
printf("Invalid virtual address\n");
return -1;
}
int frame_number = page_table[page_number].frame_number;
int physical_address = frame_number * PAGE_SIZE + offset;
return physical_address;
}
int main() {
int virtual_address = 0x1234;
int physical_address = translate_address(virtual_address);
if (physical_address != -1) {
printf("Virtual address: 0x%X\n", virtual_address);
printf("Physical address: 0x%X\n", physical_address);
}
return 0;
}
引入块表后的内存有效访问时间
在分页存储管理中,引入块表(也称为多级页表)可以显著提高内存的有效访问时间,尤其是在页表项数量庞大的情况下。块表通过分层结构来组织页表,减少了查找页表项所需的时间。以下是对引入块表后内存有效访问时间的详细解释和补充:
多级页表的基本概念
多级页表将页表分为多个层级,每个层级的页表存储下一级页表的地址。常见的多级页表结构包括二级页表和三级页表。多级页表的主要目的是减少页表在内存中的占用空间和查找时间。
多级页表的工作原理
多级页表通过分层次查找页表项,将虚拟地址转换为物理地址。以下是一个二级页表的示例:
虚拟地址结构
假设虚拟地址 VA
由以下几个部分组成:
- 一级页表索引
P1
- 二级页表索引
P2
- 页内偏移量
d
地址变换过程
- 取出虚拟地址:取出进程指令中的虚拟地址
VA
。 - **计算一级页表索引
P1
**:从虚拟地址中提取一级页表索引P1
。 - 查找一级页表:根据
P1
访问一级页表,获取二级页表的基地址。 - **计算二级页表索引
P2
**:从虚拟地址中提取二级页表索引P2
。 - 查找二级页表:根据
P2
访问二级页表,获取页框号F
。 - **计算偏移量
d
**:从虚拟地址中提取偏移量d
。 - **生成物理地址
PA
**:将页框号F
和偏移量d
组合,生成物理地址PA
。
有效访问时间的计算
引入块表后的内存有效访问时间可以表示为:
有效访问时间查找块表时间查找页表时间访问内存时间
1. 查找块表时间
查找块表的时间取决于块表的层级和哈希函数的性能。对于多级页表,每一级页表的查找时间都需要考虑。
2. 查找页表时间
查找页表的时间包括一级页表和二级页表的查找时间。通过适当的哈希函数和块表设计,可以有效地减少页表的搜索时间。
3. 访问内存时间
访问内存的时间是指最终访问物理内存的时间。
示例计算
假设:
- 查找一级页表时间为
T1
- 查找二级页表时间为
T2
- 访问内存时间为
Tm
则有效访问时间 EAT
为:
EAT=T1+T2+Tm
多级页表的实现示例
以下是一个简单的实现示例,展示了如何通过二级页表将虚拟地址转换为物理地址:
#include <stdio.h>
#define PAGE_SIZE 4096 // 页大小
#define P1_MASK 0xFFC00000 // 一级页表索引掩码
#define P2_MASK 0x003FF000 // 二级页表索引掩码
#define OFFSET_MASK 0x00000FFF // 偏移量掩码
typedef struct {
int frame_number;
int valid; // 页表项是否有效
} PageTableEntry;
typedef struct {
PageTableEntry entries[1024];
} PageTable;
PageTable first_level_page_table;
PageTable second_level_page_table[1024];
int translate_address(int virtual_address) {
int p1_index = (virtual_address & P1_MASK) >> 22; // 计算一级页表索引
int p2_index = (virtual_address & P2_MASK) >> 12; // 计算二级页表索引
int offset = virtual_address & OFFSET_MASK; // 计算偏移量
if (!first_level_page_table.entries[p1_index].valid) {
printf("Invalid virtual address\n");
return -1;
}
int second_level_base = first_level_page_table.entries[p1_index].frame_number;
if (!second_level_page_table[second_level_base].entries[p2_index].valid) {
printf("Invalid virtual address\n");
return -1;
}
int frame_number = second_level_page_table[second_level_base].entries[p2_index].frame_number;
int physical_address = frame_number * PAGE_SIZE + offset;
return physical_address;
}
int main() {
// 初始化页表
first_level_page_table.entries[0].frame_number = 0;
first_level_page_table.entries[0].valid = 1;
second_level_page_table[0].entries[0].frame_number = 0x002;
second_level_page_table[0].entries[0].valid = 1;
int virtual_address = 0x1234;
int physical_address = translate_address(virtual_address);
if (physical_address != -1) {
printf("Virtual address: 0x%X\n", virtual_address);
printf("Physical address: 0x%X\n", physical_address);
}
return 0;
}
两级页表和多级页表
在大型系统中,进程的地址空间可能非常大,直接使用单级页表会导致页表非常庞大,难以有效管理。因此,采用两级页表或多级页表是为了避免单级页表无法存储在内存中的问题。通过将页表分层,可以高效地管理和存储巨大的页表,提高内存利用率和系统性能。
两级页表(Two-Level Page Table)
两级页表通过将单级页表分为两级,以便更好地管理和利用内存。
-
两级页表的结构
- 页目录表(Page Directory Table):存储页表的入口地址,每个表项指向一个页表。
- 页表(Page Table):存储虚拟页号和物理页框号的映射。
-
地址变换过程
- 逻辑地址分解:假设一个32位的逻辑地址
A
,则地址可以分为以下三部分:- 页目录索引(Page Directory Index):高位部分,用于在页目录表中查找对应的页表入口。
- 页表索引(Page Table Index):中间部分,用于在页表中查找物理页框号。
- 页内偏移量(Page Offset):低位部分,用于计算具体的物理内存地址。
示例:
- 逻辑地址分解:假设一个32位的逻辑地址
function twoLevelAddressTranslation(logicalAddress) {
pageDirectoryIndex = getPageDirectoryIndex(logicalAddress);
pageTableIndex = getPageTableIndex(logicalAddress);
pageOffset = getPageOffset(logicalAddress);
pageTableBaseAddress = pageDirectoryTable[pageDirectoryIndex];
pageFrameNumber = pageTable[pageTableBaseAddress][pageTableIndex];
physicalAddress = pageFrameNumber * pageSize + pageOffset;
return physicalAddress;
}
多级页表(Multi-Level Page Table)
多级页表是两级页表的扩展,适用于地址空间更大的系统。通过增加页表的层级,进一步优化内存使用和页表管理。
-
多级页表的结构
- 多级页表通过将页表分成更多层级,每个层级存储下一级页表的入口地址,直到最后一级页表存储虚拟页号和物理页框号的映射。
-
地址变换过程
- 逻辑地址分解:假设一个32位的逻辑地址
A
,可以分为以下几部分:- 页目录索引(Page Directory Index):最高位部分,用于在页目录表中查找下一层级页表的入口。
- 中级页表索引(Intermediate Page Table Index):中间位部分,用于查找下层页表的入口。
- 最低级页表索引(Lowest-Level Page Table Index):次低位部分,用于查找物理页框号。
- 页内偏移量(Page Offset):最低位部分,用于计算具体的物理内存地址。
示例:
- 逻辑地址分解:假设一个32位的逻辑地址
function multiLevelAddressTranslation(logicalAddress) {
level1Index = getLevel1Index(logicalAddress);
level2Index = getLevel2Index(logicalAddress);
// Additional levels can be added as needed
pageOffset = getPageOffset(logicalAddress);
level2TableBaseAddress = level1Table[level1Index];
pageTableBaseAddress = level2Table[level2TableBaseAddress][level2Index];
pageFrameNumber = pageTable[pageTableBaseAddress][pageTableIndex];
physicalAddress = pageFrameNumber * pageSize + pageOffset;
return physicalAddress;
}
优点和应用
- 内存节约:通过分层管理页表,避免了存储大量空白无用条目的浪费,节约了内存。
- 扩展性好:多级页表可以根据地址空间需求增加层级,非常适合在大地址空间中使用。
- 减少碎片:内存动态分配更加灵活,有助于减少内存碎片,提高内存使用效率。
缺点和挑战
- 访问开销:增加了查找页表的层级,导致内存访问时需要更多的查找步骤,增加了访问开销。
- 复杂性:实现和管理多级页表相对复杂,尤其是在大地址空间和多任务环境下,需要更高效的管理机制。
- 性能优化:需要引入快速查找机制(如TLB,Translation Lookaside Buffer)来加速多级页表的地址转换,否则会影响系统性能。
反置页表
反置页表(Inverted Page Table)是一种用于虚拟内存管理的数据结构,它与传统的页表相比,采用了相反的地址映射方式。传统页表是将虚拟地址映射到物理地址,而反置页表则是将物理地址映射到虚拟地址。
工作原理
反置页表的核心思想是为每个物理页框分配一个页表项,并存储该物理页框所包含的虚拟页号。当需要进行地址转换时,系统会先获取物理地址,然后根据该物理地址在反置页表中找到相应的页表项,最后从页表项中获取虚拟页号。
优点
反置页表具有以下优点:
- 高效的地址转换:由于每个物理页框都有一个单独的页表项,因此在进行地址转换时,只需要访问一个页表项就可以直接获取虚拟页号,从而减少了搜索时间,提高了地址转换效率。
- 适用于大页表:对于具有大页表的进程,反置页表可以显著提高地址转换效率。因为在传统页表中,需要遍历整个页表才能找到匹配的页表项,而反置页表只需要访问一个页表项即可,因此可以有效减少搜索时间。
- 适用于稀疏地址空间:对于具有稀疏地址空间的进程,反置页表也可以提高地址转换效率。因为在稀疏地址空间中,只有少部分虚拟页被映射到物理页框,因此反置页表只需要访问那些被映射的页框的页表项即可,而传统页表则需要遍历整个页表,即使其中大部分页表项都是空的。
缺点
反置页表也存在一些缺点:
- 增加了内存开销:反置页表需要为每个物理页框分配一个页表项,因此与传统页表相比,增加了内存开销。
- 增加了页表管理的复杂性:反置页表需要维护一个额外的页表数据结构,因此增加了页表管理的复杂性。
应用场景
反置页表通常应用于以下场景:
- 具有大页表的系统:例如,在大型服务器系统中,通常会使用大页表来提高内存管理效率。在这种情况下,反置页表可以显著提高地址转换效率。
- 具有稀疏地址空间的进程:例如,在一些图形应用程序中,只有少部分虚拟页被映射到物理页框。在这种情况下,反置页表也可以提高地址转换效率。
结语
分页存储管理方式是一种 commonly used 的内存管理技术,它通过将地址空间划分为页,实现了虚拟内存和内存共享。地址变换机构负责将虚拟地址转换为物理地址,引入块表可以进一步提高内存的访问效率。两级页表和多级页表适用于地址空间较大的系统,而反置页表提供了高效的地址转换方式。了解分页存储管理方式,有助于我们理解虚拟内存和内存管理的技术细节。