20.内存换入-请求调页
Posted PacosonSWJTU
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20.内存换入-请求调页相关的知识,希望对你有一定的参考价值。
【README】
1.本文内容总结自 B站 《操作系统-哈工大李治军老师》,内容非常棒,墙裂推荐;
2.操作系统关于内存管理的核心是基于虚拟内存的分段和分页管理;
3.而用内存换入和换出实现虚拟内存 ;
【1】虚拟内存
1)段页同时存在的内存管理图像
2)用户眼里的内存(就是虚拟内存)
【图解】
- 用户可以随意使用该内存(虚拟内存),就像单独拥有4G内存;
- 每个进程都有对应一个虚拟内存;
- 虚拟内存中存放了程序段,包括数据段,代码段,栈段等;
- 虚拟内存如何映射到物理内存,用户全然不知道;
- 操作系统利用虚拟内存与物理内存的映射来给用户提供这种4G内存的假象或图像;
3)换入
换入就是在虚拟内存与物理内存的映射上进行工作;
【2】用换入换出实现大内存
- 问题:虚拟内存4G,但物理内存实际只有1G,怎么办 ?
- 解决方法:内存换入换出;
【2.1】换入实例
【图解】
- 用户程序都在磁盘上,包括它的代码段,数据段,共计4G大小;
- 场景1:执行到0~1G,操作系统把磁盘上0~1G的代码段 换入 到物理内存,并建立虚拟内存与物理内存的映射;用户可以随便使用0~1G虚拟内存空间;
- 场景2:执行到3G~3.5G,操作系统把磁盘上3G~3.5G的数据段 换入 到物理内存,并建立虚拟内存与物理内存的映射;用户可以随便使用3G~3.5G虚拟内存空间;
【小结】
1)以上2个场景,用户感觉到 可以使用 0~1G范围的内存,也可以使用3G~3.5G范围的内存,用户感觉机器可以使用的内存空间是0~4G(但实际物理内存只有1G,却给了用户4G内存的假象);
2)请求的时候才映射(指令执行时需要寻址到3G~3.5G)
请求的时候才换入,用这种方法可以实现 0~4G 的一个规整的虚拟内存;
【2.2】请求调页(换入)
1)请求换入,建立映射(虚拟内存与物理内存的映射 );
- 即 请求调入物理内存页,并建立虚拟内存与物理内存页的映射;
【图解】请求调页具体步骤
指令的逻辑地址为 段号+偏移(cs:ip);
- 步骤1: 操作系统执行指令 load [addr], 根据逻辑地址addr的段号从段表中查询段基址(虚拟内存地址);
- 步骤2: MMU根据段基址计算出逻辑页号,发现逻辑页号在页表中没有记录(如拿着3.5G内存地址的逻辑页号 到 0~1G中的页表查找 物理页号,肯定查无记录);
- 步骤3: 因为页表中没有记录(缺页),或MMU发现缺页,则MMU发出页错误中断请求(缺页中断请求)(设置中断请求触发器的某1位为1),等待cpu处理;
- 步骤4: CPU处理中断,执行中断处理程序,即执行页错误处理程序;
- 步骤5: 页错误处理程序从磁盘找到需要的页,并读入到空闲物理内存页,并建立虚拟内存与物理内存的映射关系(在页表中添加一条记录);
- 步骤6: 中断处理程序执行完成后,即请求调页完成后,CPU再次执行 load [addr] 指令,就能够正确把逻辑地址 addr 翻译到物理地址了;
综上:应用请求调页或物理内存页换入,就可以实现4G大小的虚拟内存供用户随便使用;
- 补充1:MMU是一个硬件,可以代替程序把逻辑地址立即转为物理地址(相比软件,MMU硬件的地址翻译效率更高);
- 补充2:虽然对用户来说感觉到的是4G内存,但使用过程中存在系统响应慢的情况,原因在于访问某些内存地址空间时,由于缺页可能导致操作系统请求调页(需要从磁盘读取数据到物理内存,比较耗时),调页需要耗费时间的;
2)问题:采用请求调页而不是请求调段,原因是什么 ?
段比页占用内存空间更大,请求调页占用内存空间小,内存利用率高;
当然,也可以请求调段,只不过性能可能偏低;
【3】请求调页代码实现
1)一个实际系统的请求调页
请求调页代码,从缺页中断开始; 14号中断是缺页中断;
// 操作系统启动时,初始化14号中断(缺页中断)的中断处理程序为 page_fault
void trap_init(void)
set_trap_gate(14, &page_fault);
// 修改idt表,新增中断与中断处理程序的关联关系
#define set_trap_gate(n, addr)
_set_gate(&idt[n], 15, 0, addr);
2)处理中断 page_fault
【图解】
- cr2寄存器: 用于存储页错误线性地址,记录是哪一页缺页了;(线性地址就是虚拟地址)
- Call _do_no_page: 调用缺页处理函数 _do_no_page(),参数是 %cr2寄存器的值(注意压栈了)
3)缺页函数 do_no_page()
从磁盘中找到需要的页,并读入空闲的物理内存页,建立虚拟内存与物理内存的映射关系(页表);
// 缺页函数 do_no_page()
// 在 linux/mm/memory.c 中
void do_no_page(unsigned long error_code, unsigned long address)
// 获取逻辑页号
address &= 0xfffff000;
tmp = address - current->start_code; // 页面对应偏移量
if( !current->executagle || tmp >= current->end_data )
get_empty_page(address); return ;
// 换入第1步: 申请一个物理空闲页
page = get_free_page();
// 换入第2步: 从磁盘(或当前进程可执行文件所在设备 i_dev)上读取缺页数据到物理空闲页 (block read)
bread_page(page, current->executable->i_dev, nr);
// 换入第3步: 把物理页与虚拟地址建立映射(在页表中新增一条映射记录)
put_page(page, address);
补充:请求调页换入的3个步骤(非常重要*):
- 步骤1:申请空闲的物理内存页;
- 步骤2: 从磁盘读取缺页数据到物理内存页;
- 步骤3: 建立虚拟内存与物理内存页的映射关系,即修改页表,新增一条映射记录;
4)put_page() 修改页表,新增一条映射记录 (仅了解)
以上是关于20.内存换入-请求调页的主要内容,如果未能解决你的问题,请参考以下文章
:内存管理 -- 虚拟内存的实现:请求分页管理方式页面置换算法(决定应该换入哪页换出哪页:OPT先进先出最近最久未使用时钟)