1、为什么需要虚拟内存
虚拟内存是现代操作系统的核心功能之一,它解决了物理内存管理的多个关键问题:
内存隔离与保护: 在没有虚拟内存的系统中,所有进程都直接访问物理内存。一个进程的错误操作可能破坏其他进程甚至操作系统的数据。虚拟内存为每个进程提供独立的地址空间,实现真正的进程隔离。
简化内存管理:程序员无需关心物理内存的实际布局。每个进程都拥有从0开始的连续地址空间,编译器链接器可以生成固定的内存布局。
突破物理内存限制:通过分页机制,可以将暂时不用的内存页换出到磁盘,当需要时再换入,使得程序可以使用比实际物理内存更大的地址空间。
高效的内存共享:不同的进程可以映射到相同的物理页,实现代码和数据的共享,如共享库机制。
2、如何实现虚拟内存
虚拟内存的核心实现机制是分页。x86 架构 32 位采用二级页表结构:
虚拟地址: 程序使用的地址,通常为32位或64位
物理地址:实际内存硬件的地址
页:内存管理的基本单位,通常为4KB
页表:存储虚拟页到物理页映射关系的数据结构
MMU:内存管理单元,负责地址转换,32 位虚拟地址分解为:
页目录索引(10位)
页表索引(10位)
页内偏移(12位)
CPU 通过 CR3 寄存器定位页目录,经过二级查表将虚拟地址转换为物理地址。
MMU工作流程
3、手写实现虚拟内存管理
static page_mapping_table_t* find_create_page_mapping_table(page_mapping_dir_t* page_dir, void* virtual_addr) {
page_mapping_dir_t* target_page_dir = (page_dir + PDE_INDEX(virtual_addr));
page_mapping_table_t* target_page_table = nullptr;
if (target_page_dir->present) {
// 大部分同学可能都看不懂
target_page_table = (page_mapping_table_t*)(target_page_dir->phy_pt_addr << 12);
} else {
target_page_table = alloc_a_page();
target_page_dir->present = 1;
target_page_dir->read_write = 1; // 代码段
target_page_dir->user_mode_acc = 0; // 只能内核访问
target_page_dir->phy_pt_addr = (u32_t)target_page_table >> 12;
}
return target_page_table + PTE_INDEX(virtual_addr);
}
void create_memory_mapping(page_mapping_dir_t* page_dir, void* virtual_addr, void* physics_addr, u32_t count) {
for (size_t i = 0; i < count; i++)
{
page_mapping_table_t* page_mapping_table = find_create_page_mapping_table(page_dir, virtual_addr);
page_mapping_table->phy_page_addr = (u32_t)physics_addr >> 12;
assert(page_mapping_table->present == 0);
page_mapping_table->present = 1;
page_mapping_table->read_write = 1; // 代码段
page_mapping_table->user_mode_acc = 0; // 只能内核访问,后面再写进程加载进入用户态,会闪退
virtual_addr += PAGE_SIZE;
physics_addr += PAGE_SIZE;
}
}
// 打开虚拟内存,建立内核的初始化映射
static void init_virtual_memory_mapping() {
page_mapping_dir_t* page_table_dir = alloc_a_page();
// 初始化先建立 4M 的内存映射,并且前 4M 的虚拟内存映射到真实(物理)内存的前 4M
create_memory_mapping(page_table_dir, 0, 0, KERNEL_INIT_MAPPING_SIZE / PAGE_SIZE);
// 设置 cr3 寄存器(页目录地址)
set_cr3(page_table_dir);
// 打开 mmu 虚拟映射硬件
open_cr0_enable_page();
}
4、手写实现缺页异常
void do_interrupt_handler_page_fault(exception_frame_t* exception_frame) {
printk("do_interrupt_handler_page_fault\r\n");
// 哪一块缺页了呢?
void* page_fault_addr = (void*)read_cr2();
// 虚拟地址要往下对齐 4kb
void* virtual_addr = (void*)DOWN_ON(page_fault_addr, PAGE_SIZE);
page_mapping_dir_t* page_mapping_dir = (page_mapping_dir_t*)read_cr3();
// 新的物理页映射地址
void* new_physics_addr = alloc_a_page();
create_memory_mapping(page_mapping_dir, virtual_addr, new_physics_addr, 1);
flush_tlb(page_fault_addr);
}
5、用户进程虚拟内存管理
用户进程虚拟内存管理.png












网友评论