内存管理(哈工大李志军)

计算机如何工作?

在这里插入图片描述

将程序进入内存

在这里插入图片描述
这个程序必须从实际物理地址0开始才可执行。

重定位:修改程序中的地址(相对地址)

在这里插入图片描述
相当于加入了基址。

什么时候完成重定位?

  • 编译时:编译时重定位得程序只能放在内存固定位置
  • 载入时:重定位的程序一旦载入内存就不能动了
  • 运行时

交换:程序载入后移动

在这里插入图片描述

运行时重定位

在这里插入图片描述
在这里插入图片描述

  • 首先程序编译完成,执行:
  • 创建进程1和进程2,各找到一段空闲内存,并把各地址作为 base 赋给各进程的PCB
  • 程序载入到 base 为基址的内存
  • 进程1 switch_to 切换到进程2,要将基地址更改为进程2 PCB 中的base
    在这里插入图片描述

内存分段

在这里插入图片描述
在这里插入图片描述
GDT 是全局描述符表,只有一个
进程切换时查LDT (局部描述符表),LDT 在 进程的PCB 中
在这里插入图片描述

内存分区

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

此时再次申请 reqSize = 40K,选择哪一个?
在这里插入图片描述

  • 首先适配:分割最先遇到符合大小的分区,速度快
  • 最佳适配:每次都分割最接近的,会剩下更小的空闲分区,形成内存碎片。
  • 最差适配:分割最大的分区,最后得到较为均匀的分区
    在这里插入图片描述
    答案是 B

分页

可变分区的问题:
在这里插入图片描述
在这里插入图片描述
将内存分割固定大小的页,像面包切片一样,假设页大小为 4K ,程序请求内存按页分配,那么最多浪费不超过4K。

在这里插入图片描述
mov [0x2240],%eax

  1. 0x2240 % 0x1000(4K) == 0x2240 >> 12 ,得到页号,这一步是由 MMU 硬件完成。
  2. 逻辑地址 :0x02 : 0x240 ,查页表,页表指针在PCB中
  3. 由页号查到页框号为3,物理地址为0x3240

多级页表和快表

页表的缺点:分页小,会导致页表太大
页面尺寸为 4K, 32位地址,4G内存会有 1M 的页表项,需要占用 4M内存,并发10个进程需要40M内存
在这里插入图片描述

在这里插入图片描述
如果页表不连续,这种方式效率会很低,每执行一次指令都需要查找;如果页表连续,则会造成浪费。

多级页表

在这里插入图片描述
类似于书中的章和节,这样遍历时可以按章节跳过,而不是一页一页翻。

  • 页目录有 2102^{10}个页表指针,占2104=4K2^{10} * 4 = 4K字节
  • 每个页表有2102^{10}个页,页表占2104=4K2^{10} * 4 = 4K字节,指向了2102104=4M2^{10}*2^{10} * 4 = 4M的空间。
  • 我们看到图中页目录用了三个页表,那么这个程序使用了12M内存。
  • 考虑这个机制和之前单页表的占用对比:页目录占用 4K,而页表不需要都驻留内存,这个程序使用了三个页表,共12K,总共占用4K + 12K= 16K的内存,远小於单页表情况下的4M的内存。
  • linux 0.11 中:

在这里插入图片描述

快表

在这里插入图片描述
在32位系统上,使用多级页表会增加一次访存,但在64位系统上,使用多级页表会导致增加多次访存。
在这里插入图片描述
在这里插入图片描述

段页结合的实际内存管理

在这里插入图片描述
为了将分段机制和分页机制结合起来,引入了虚拟内存的概念。
用户侧为分段机制,物理侧做分页机制

在这里插入图片描述

实际的段页式内存管理

在这里插入图片描述
在这里插入图片描述
1. 首先将虚拟内存分割出一块区域作为用户的代码段、数据段,可以使用分区方法。
在这里插入图片描述
2. 将虚拟内存中的段分割,与物理内存的页做关联。

在这里插入图片描述
3. 执行mov [300],首先查段表找到虚拟基址0x45000,并得到虚拟地址0x45300
在这里插入图片描述
4. 查页表第 0x45 项,得到页框号7,得到物理基址0x7000
5. 得到物理地址0x7300
在这里插入图片描述
fork( ):
在这里插入图片描述
copy_mem()分配虚拟内存,设置段表,参数p为进程的 PCB,new_data_base = nr * 0x4000000相当于对内存做了分割,每个进程占用64M虚拟地址空间,接下来两行完成了对段表的填写。
在这里插入图片描述

分配内存(跳过),建页表:
在这里插入图片描述
copy_page_tables参数为虚拟地址,from_dir是父进程的虚拟地址
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 进程1 是 进程2 的父进程,但映射的已经不是同一地址了。
  • 当执行 *p = 7时,首先 p0x300 ,接着通过查 LDT 中的段表得到虚拟地址 0x00400300,虚拟地址再通过页表找到物理内存。

内存换入

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 执行load [addr],当缺页时,产生中断,启动页错误处理程序,从磁盘中读入到一空闲页中,并做好页表和物理内存的映射,并重新执行load [addr],MMU会使PC保持不动而不是PC+ 1执行下一指令。
    在这里插入图片描述
    答案是 C

实际系统的请求调页

在这里插入图片描述
系统初始化时就已经设置好中断处理。
在这里插入图片描述
中断发生,保护现场所以有一堆push,最后一行将页错误线性地址作为参数压入栈,调用 do_no_page()
在这里插入图片描述
申请一个空页,并从磁盘中读入,调用put_page(page,address)建立映射。
在这里插入图片描述

内存换出

内存换入时,会调用get_free_page()来取得新的空页,可内存是有限的,怎么处理?
在这里插入图片描述

FIFO 页面置换

在这里插入图片描述

MIN 页面置换

选最远将使用的页淘汰,是最优方案
在这里插入图片描述

LRU 页面置换

选最近最长一段时间没有使用的页淘汰(最近最少使用)
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 每次访问一页时,自动设置为 1
  • 当选择淘汰页时,扫描该位,如果是 1 则清 0(再给一次机会),并继续扫描,是0时淘汰该页。
    在这里插入图片描述
  • 如果缺页很少,很有可能所有的 R 都会被置为 1 ,如果再发生缺页,将会转完一圈再淘汰当前页,退化为 FIFO
  • 定时清除R位,放入时间中断中,慢指针放入到选择淘汰中。

在这里插入图片描述
红框内是颠簸原因的解释,优化此问题需要 工作集 概念的引入

内存换入换出总结

Linux 中的 swap 分区
在这里插入图片描述

总结

  • 内存换入换出是虚拟内存实现的核心概念

  • 虚拟内存是段页实现的核心概念

  • 段页结构是一个程序执行的核心概念

  • 一个程序的执行离不开进程的概念

  • 内存管理机制名词都是类似书中的:*段,页,节(页表),章(页目录表)

  • 逻辑内存地址和物理内存地址的关系,更像是一本书的章节号和这本书的第几个字的关系。
    所以正常人找到书中一句话不会从头开始数吧,效率太低了。怎么说也要找到第几章,第几节,第几页,第几段,这样范围大大缩小。

  • 段是面向用户,用户直接面对的地址也是虚拟内存,方便管理。

  • 页是面向机器,分页机制细度更高,因为内存分配是不连续的,分页机制解决降低内存使用率低下,效率更高。

  • 段页结合就需要虚拟内存概念的引入

  • 虚拟内存会给进程一个连续的内存模型,但物理实现是离散的

  • 当进程访问某个虚拟地址的时候,就会先去看页表,如果发现对应的数据不在物理内存上,就会发生缺页异常

  • 缺页异常的处理过程,操作系统立即阻塞该进程,并将硬盘里对应的页换入内存,然后使该进程就绪,如果内存已经满了,没有空地方了,那就找一个页覆盖,至于具体覆盖的哪个页,就需要看操作系统的页面置换算法是怎么设计的了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章