赞
踩
实现内存检测,理解Linux内存管理,实现101012分页
参考:
检测内存容量
趣谈 Linux 操作系统
内存管理
《操作系统真相还原》
BIOS 中断 0x15 的子功能够获取0xE820 能够获取系统的内存布局,由于系统内存各部分的类型属性不同,
BIOS 就按照类型属性来划分这片系统内存,所以这种查询呈迭代式,每次 IOS 只返回一种类型的内存
信息,直到将所有内存类型返回完毕。子功能 OxE820 的强大之处是返回的内存信息较丰富,包括多个属
性字段,所以需要一种格式结构来组织这些数据。内存信息的内容是用地址范围描述符来描述的,用于存
储这种描述符的结构称之为地址范围描述符( Address Range Descriptor Structure, ARDS )
typedef struct {
unsigned int base_addr_low;
unsigned int base_addr_high;
unsigned int length_low;
unsigned int length_high;
unsigned int type;
}ARDS;
其中的 Type 宇段用来描述这段内存的类型,这里所谓的类型是说明这段内存的用途,即其是可以被操作系统使用,还是保留起来不能用。
BIOS 中断 0x15 的子功能 0xE820 是一个用于获取系统物理内存布局的功能。它可以检测系统中可用或不可用的内存地址范围以及其属性,包括内存大小、类型(例如 RAM、ROM、ACPI 等)、保留位等。该子功能通常用于可引导操作系统、虚拟机管理软件和其他需要在系统启动时了解内存布局信息的应用程序。
该子功能由以下输入参数和输出参数组成:
使用该子功能的步骤通常为:
memory_check: xor ebx, ebx ; 第一次调用前 需要初始化为0 mov di, BIOS_ARDS_CACHE ; es:di 将BIOS获取到的内存信息写到这里 .smap_check: mov eax, 0xe820 mov ecx, 20 mov edx, 0x534d4150 ; smap int 0x15 jc check_memory_error ; cf = 1 add di, cx ; 移动写入的字节数 inc dword[MEM_CEHCK_TIMES] ; 检测次数 + 1 cmp ebx, 0 ; cf = 0, ebx会被bios修改,ebx不为0就要继续检测 jne .smap_check mov ax, [MEM_CEHCK_TIMES] mov [BIOS_ARDS_TIMES], ax .check_memory_success: mov si, check_memory_success_msg call print
内存都被分成一块一块儿的,都编好了号。当程序要访问虚拟地址的时候,由内核的数据结构进行转换,转换成不同的物理地址,这样不同的进程运行的时候,写入的是不同的物理地址,这样就不会冲突了。
内存管理系统精细化为下面三件事情:
02.加载GDT表,进入保护模式中讨论过了分段机制。
存在不足:
从虚拟地址到物理地址的转换方式,称为分页(Paging)。 对于物理内存,操作系统把它分成一块一块大小相同的页,这样更方便管理,例如有的内存页面长时间不用了,可以暂时写到硬盘上,称为换出。一旦需要的时候,再加载进来,叫作换入。这样可以扩大可用物理内存的大小,提高物理内存的利用率。 这个换入和换出都是以页为单位的。页面的大小一般为 4KB。为了能够定位和访问每个页,需要有个页表,保存每个页的起始地址,再加上在页内的偏移量,组成线性地址,就能对于内存中的每个位置进行访问了。
动分页机制后,应用程序就看不到机器的物理内存了,它看到的是一块虚拟的,不存在的内存空间。这块虚拟的内存空间也是从0开始的,到2^32-1。
这块虚拟内存空间也是划分成了一个个4KB的内存页。操作系统会通过页表将这些在虚拟内存空间中连续的内存页,从物理内存空间中找空闲的物理页,建立一个映射关系。这些物理页之间可以是不连续的。
如此以来,就可以将多个不连续的物理页,变成了进程使用的连续内存页。这样进程就感觉自己在使用连续的内存页,而实际最终在物理内存的表现上却是实现不连续的内存页
虚拟地址分为两部分,页号和页内偏移。页号作为页表的索引,页表包含物理页每页所在物理内存的基地址。这个基地址与页内偏移的组合就形成了物理内存地址。虚拟内存中的页通过页表映射为了物理内存中的页。
在操作系统中,PDT、PTT、PDE和PTE都是页表相关的数据结构,用于实现虚拟内存管理。它们的具体含义如下:
在进行地址转换时,操作系统会首先根据虚拟地址的高 10 位找到对应的 PDT 表项,然后根据中间 10 位找到对应的 PTT 表项,最后根据低 12 位找到物理页的基地址。通过这种方式将虚拟地址映射到了物理地址,从而实现了内存管理的功能。
CR3中含有页目录表物理内存基地址,因此该寄存器也被称为页目录基地址寄存器PDBR(Page-Directory Base address Register)
10-10-12 多级分页机制是一种用于管理虚拟地址和物理地址映射关系的数据结构,它将虚拟地址空间划分为多个层次,每一层都使用单独的页表进行维护。具体来说,它将一个 32 位的虚拟地址分为三个部分:
前 10 位为页目录项索引,中间 10 位为页表项索引,后 12 位为页内偏移量。
PDT 和 PTE 是分页机制中的两个重要数据结构。它们分别代表着页目录表和页表中的一个条目,用于进行虚拟地址到物理地址的映射。在实现分页机制时,PDT 和 PTE 中会设置一些属性位,用于控制页面的访问权限、页面的映射关系、页面的使用状态等。下面是 PDT 和 PTE 中常见的一些属性及其含义:
属性 | 位数 | 含义 |
---|---|---|
Present/有效位 | 1 | 当PDE或PTE中有一个的属性P=0时,物理页就是无效的。 |
W/R 位 | 0 表示只读。1表示可读可写 | |
User/Supervisor | 1 | 用户/特权位,用于控制该页或页表是否仅对特权级(管理员)访问。0 表示只有特权用户才能访问(0到2环),1表示普通用户也可以访问(3环) |
Accessed/访问位 | 1 | 表示该页或页表是否被访问过,如果为 1,则表示该页或页表已经被访问过,0 表示该页未被访问 |
Dirty/脏位 | 1 | 表示该页是否被修改过,0表示该页未写过,1表示该页被写过 |
PS(Page Size)位 | 1 | 标志为 0,则表示页面大小为 4KB,如果 PS 为 1,则表示页面大小为 4MB |
Global/全局位 | 1 | 全局位用于控制页面的全局性,如果该位为 1,则表示该页面是全局可见的,可以被进程之间共享 |
PDE 属性表格:
31 - 12 | 11 - 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
页表基址 | 有效 | G | PS | 0 | A | PCD | PWT | U / S | R / W | P |
PTE 属性表格:
31 - 12 | 11 - 9 | 8 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
---|---|---|---|---|---|---|---|---|---|---|
页表基址 | 有效 | G | PAT | D | A | PCD | PWT | U / S | R / W | P |
页表基址是地址的高20位,最终加上偏移量12位,20+12 = 32位,完成32位寻址。
当操作系统启动时,分页机制并没有被启用,因此所有的内存地址都是物理地址。为了启用分页机制,下面是一般的详细步骤:
// 分配一个物理页框作为PDT表 uint * PDT = (uint*)PDT_START_ADDR; // 清零 memset(PDT, 0, 4 * 1024); // 外层循环遍历PDE表 for (int i = 0; i < 16; i++) { // 4M * 16 = 64M // 分配一页内存作为PTT int PTT = (int)PDT_START_ADDR + ( (i + 1) * 0x1000); // addr + 4k // 将PDT的地址填充到PDE表项中,同时设置相关标志位 pt_entry PDE = PTT | PDT_PRESENT | PDT_READWRITE | PDT_USER; // U/S = 1, R/W = 1, P = 1 PDT[i] = PDE; pt_entry* ptt_arr = (pt_entry*)PTT; if (i == 0) { // 第一块映射区,给内核用 // 0~1024 *4k =4m for (int j = 0; j < 1024; ++j) { int* item = &ptt_arr[j]; int virtual_addr = j * 0x1000; *item = virtual_addr | PDT_PRESENT | PDT_READWRITE | PDT_USER; } } }
inline void set_cr3(uint v) {
asm volatile("mov cr3, eax;" ::"a"(v));
}
inline void enable_page() {
asm volatile("mov eax, cr0;"
"or eax, 0x80000000;"
"mov cr0, eax;");
}
当CPU要访问一个虚拟地址时,首先会查找该地址所在的页表,然后根据页表中的物理地址进行内存访问。如果该虚拟地址还没有被映射到物理地址上,访问就会失败,导致进程出现错误或崩溃。
除此之外,操作系统还需要定期地更新页表,根据程序的运行情况来调整页表的映射关系,以优化内存使用效率和系统性能。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。