目录
前言
每个进程都有自己的内存地址空间,该空间其实都是虚拟地址,要借助页表来映射到实际物理内存地址中。而真实的页表是如何的呢?
之前介绍过OS和磁盘文件进行IO的基本单位是4kb = 八个扇区;而OS对内存的管理工作基本单位也是4kb;
OS进程内存管理,不是以字节为单位,而是以内存卡为单位,默认大小是4kb;
页框
页框(Page Frame):
是物理内存中的固定大小的块,通常为4KB。在物理内存中,页框是用于存储实际数据的最小单元。假设内存大小为4GB,也就能划分出1024*1024个页框。当进程通过页表来访问物理地址时,其本质就是找到页框的地址。
操作系统对于物理内存的管理变成了对页框块的管理;一个页框是什么呢?❓
在Linux内核中,使用struct page结构来描述内存中的每一页框。这个结构体定义在内核头文件include/linux/mm_types.h中。该结构体通常有以下字段:flags:标志位,表示页面的各种状态;
- mapping:指向该页框所属的地址空间
- index:该页框在地址空间中的偏移量
- _maocount:记录该页框到页表条目数
- _refcount:页面引用计数。表示有多少用户正在使用该页框
struct page {
void *address; // 物理地址
unsigned long flags; // 页面标志(例如是否在使用、是否脏等)
struct page *next; // 指向下一个页面的指针(可能用于链表)
unsigned int usage; // 页面使用计数
unsigned int refcount; // 引用计数
unsigned int type; // 页面类型
unsigned int index; // 页面索引
};
页表
页表是用于存储物理内存与虚拟内存地址之间的映射关系的结构。每个进程都有属于自己的页表。页表是由页目录和页表项组成的。
每个虚拟地址都是 32个比特位,即 0000 0000 0000 0000 0000 0000 0000 0000;
前面10个比特位存储的是页目录;
中间10是二级页表;
最后剩下的12个比特位作为偏移量(对应着二级页表指向页框起始地址处后进行偏移)
寻址如下:
- 虚拟地址的前10个比特位在页目录当中进行查找,找到对应的页表(页目录用于索引相应的页表),2^10字节 = 1KB,存储页目录所需的内存是 1KB,且页目录只有一张;
- 再选择虚拟地址中间的10个比特位在对应的页表当中进行查找,找到物理内存中对应页框的起始地址,每张页表的大小也是 2^10字节 = 1KB,页表是有多张的;
- 最后将虚拟地址中剩下的12个比特位作为偏移量从对应页框的起始地址处向后进行偏移,找到物理内存中某一个对应的字节数据,2^12字节 = 4KB,与物理内存的页框对应
寻址过程
MMU 接收到一个虚拟地址。
这个虚拟地址被分成三个部分:页目录索引、页表索引和页内偏移量。
MMU 使用 CR3 中的值找到页目录表的物理地址,通过页目录索引查找页目录表中的对应项,得到指向页表的物理地址,使用页表索引查找页表中的对应项,得到指向页框的物理起始地址,将页框的物理地址与页内偏移量组合,形成最终的物理地址。
实现了虚拟内存到物理内存的有效转换。