操作系统——内存

一、内存使用

内存使用:将程序放到内存中,PC指向开始地址

重定位:

  • 修改程序中的地址
  • 逻辑地址 ----> 物理地址

什么时候完成重定位?

  • 编译时(不灵活,程序只能放在内存固定位置、速度快):嵌入式系统
  • 载入时(灵活,速度相对来说慢):普通系统

 程序载入内存后,还需移动:交换(swap)

重定位最合适的时机 —— 运行时重定位

地址翻译:每执行一条指令,都要从逻辑地址算出物理地址

 内存使用过程:

  • 程序编译好(地址不改)
  • 程序执行(创建进程、创建PCB)
  • 找空闲内存
  • 空闲地址(起始地址)赋给PCB
  • PC设在起始地址
  • 执行(执行过程中每个地址都要地址翻译)

二、内存分段

        程序员眼中的程序:由若干段组成,每个段(堆栈段:向下增长;程序段只读;数据段可写;函数库……)有各自的特点、用途

  • 用户可以根据各段自身特点分治每个段
  • 程序分段放入内存符合用户观点,提升内存使用效率

定位具体指令:< 段号 (ds) : 段内偏移 >

每个进程的PCB放一堆基址,分别对应有哪些段,每个段都有基址

进程段表:PCB中用来存储每个段的基址

  • GDT:OS对应的段表
  • LDT:每个进程对应的段表

 

 程序分段使用内存:

  • 程序分段,每段放入一段内存
  • 每段基址放入LDT表,即初始化LDT表完成
  • LDT表(或者LDT表表头指针:???不确定,求解答)赋给PCB
  • 根据PC指针 + LDT表取指、执行
    • 执行每条指令时,查找LDT表,根据表中基址 + 程序中逻辑地址找到物理地址
  • 时间片用完后或其他情况引发进程切换(switch) 
  • 每段段基址写回LDT表和PCB
    • LDTR寄存器切换
    • 段选择子被装入LDTR,即LDT表描述符自动被装入LDTR
  • 根据LDTR+PC执行

LDT表、GDT表、LDTR、段选择子详解:

  • LDT表可以有若干张,每个进程可以有一张,我们可以这样理解GDT和LDT:GDT为一级描述符表,LDT为二级描述符表
  • LDT嵌套在GDT之中
  • LDTR记录局部描述符表的起始位置,LDTR的内容是一个段选择子。由于LDT本身同样是一段内存,也是一个段,所以它也有个描述符描述它,这个描述符就存储在GDT中,对应这个描述符表也会有一个选择子,LDTR装载的就是这样一个选择子
  • LDTR可以在程序中随时改变,通过使用lldt指令

 例:

如果我们想在表LDT2中选择第三个描述符所描述的段的地址12345678h

  • 首先需要装载LDTR使它指向LDT2 使用指令lldt将Selector2装载到LDTR
  • 通过逻辑地址(SEL:OFFSET)访问时SEL的index=3代表选择第三个描述符;TI=1代表选择子是在LDT选择,此时LDTR指向的是LDT2,所以是在LDT2中选择
    • 此时的SEL值为1C (二进制为11 1 00):OFFSET=12345678h。逻辑地址为1C:12345678h
  • 由SEL选择出描述符,由描述符中的基址(Base)加上OFFSET可得到线性地址,例如基址是11111111h,则线性地址=11111111h+12345678h=23456789h
  • 此时若再想访问LDT1中的第三个描述符,只要使用lldt指令将选择子Selector1装入再执行2、3两步就可以了(因为此时LDTR又指向了LDT1)

三、内存分区与分页

1.可变分区管理:在使用内存时,维护下面两个表:

  • 空闲分区表:始址、长度
  • 已分配分区表:始址、长度、标志(标记哪个进程使用该内存)

(1)当申请的内存大小,在空闲分区表中有多个内存区间满足时:

  • 最佳适配(O(n)):最接近于申请的内存大小的分区

                                        空闲分区会被分割成细小碎片

  • 最差适配(O(n)):在满足内存大小前提下,选择与申请内存相差最大的

                                        空闲分区会被均匀分割

  • 首先适配(O(1)):在空闲分区表中找到的第一个满足申请内存大小的分区

                                        表查的快、运行速度快

        如果OS中的段内存申请很不规则(有时需要很大的一个内存块,有时又很小),最佳适配算法最好

分段 ------> 虚拟内存

分页 ------> 物理地址

  • 可变分区会造成内存碎片,内存使用效率不高
  • 内存紧缩:将空闲分区合并,需要移动一个段(复制内容)
  • 内存碎片依靠内存紧缩解决,内存紧缩耗时长

2.内存分页

  • 将物理内存分成页(4K),不需要内存紧缩,一个程序段最大的内存浪费是4K(一页,无限接近于4K)
  • 页中的地址需要重定位,页表记录

 0x2240: 2240 / 4K = 商……余数

  • 商为页号(12位, 0x2240右移12位 --->2)
  • 余数为页内偏移(后3位 --->240)

0x3240: 页框号 *4K = 物理地址 = 页框号左移12位+页内偏移

        内存使用分页和内存分段使用原理相同,只是分页分的是物理内存,分段分的是程序段;分页用的不是LDT表存放相关信息,用的是页表;寄存器也为CR3寄存器

四、多级页表与块表

为提高内存空间利用率,也应该小,但是页小,页表就大了

1.页表中只存放进程用到的页:

        查找慢、运行速率低,故不可以只放进程用到的页;页表中的页号应连续,这样查找就为页表起始表头+偏移量,查找方便,但是这样占用内存多,浪费

2.多级页表:页目录表+页表

  • 2^10 * 4KB = 4M = 2^10 * 2^2 * 2^10 = 2^20 * 2^2 = 4M(2^10个页指向的内存物理空间
  • 4字节地址为32位

多级页表提高了空间利用率,但增加了访存的次数,每增加一级页表,访存次数增加1次

TLB(快表)是寄存器,一组相联快速寄存器

五、段页结合的实际内存管理

  • 段面向用户
  • 页面向硬件

 虚拟内存映射到内存这一过程对用户是透明的

1.段页同时存在的重定位

 内存管理核心:内存分配

  •  进程申请内存
  • 虚拟内存采用分区法分割部分虚拟内存给进程
  • 代码段放入虚拟内存,并建立段表(即完成段表初始化)
  • 代码段分割,放入页
  • 建立页表(即页表初始化)
  • 重定位使用内存

0x300:逻辑地址 通过段表找到虚拟内存地址 ;虚拟内存地址通过页表找到物理地址

六、代码分析

  • 每个进程占64M虚拟地址空间,互不重叠(理论上):进程不重叠,页号不重叠,可共用一套页表;实际:基本重叠

  •  from_page_table 和 to_page_table 分开,即将父子进程页表分开,两个页表内容相同

  • 父子进程共用虚拟内存
  • 父子进程各有一套页表,内容完全一样

  • 子进程在写入内存前要改写页表,实现父子进程分离
  • 父子进程LDT表不同;刚初始化时,附近进程页表内容相同;运行时,修改子进程页表,实现父子进程内存分离
  • 进程放入内存页(逻辑地址 ----> 物理地址)

七、内存换入——请求调页

实现虚拟内存需要内存换入换出

  • 用户可随意使用虚拟内存,就像单独拥有所有内存
  • 虚拟内存 通过OS映射,用户不知道这个过程:透明 转成物理内存

1.请求调页

 2.一个实际系统的请求调页

(1)请求调页,缺页中断(14号中断)

  • IDT表:中断向量表

(2)处理中断:中断现场保护(压栈)

(3)do_no_page:

  • 虚拟地址页内偏移置0(address&=0xfffff000),得到虚拟页号
  • 申请物理空闲页,得到空闲页:get_free_page
  • 从磁盘读入页到空闲页
  • 物理页、虚拟页建立映射(写页表):put_page

八、内存换出

算法评价准则:缺页次数

1.FIFO页面置换

2.MIN页面置换:理想算法,无法预测将来

  • 选择最远将使用的页淘汰,是最优方案

3.LRU页面置换:利用程序局部性,公认的很好页面置换算法 

  • 最近最常未使用的页淘汰

(1)LRU的准确实现:时间戳(time step)

  • 每页维护一个时间戳:时间戳实行时间长,时间片很可能溢出

 (2)LRU的准确实现:页码栈:时间长,代价大

LRU准确实现代价大,所以要用近似实现

(3)LRU近似实现——将时间计数变为是和否(cloak算法)

  • 每个页增加1个引用位(R位):每访问1页,引用位置为1(最近访问过)
    • 每次访问一页时,硬件自动设置该位
    • 选择淘汰页:扫描该位,为1时清0,并继续扫描,为0时淘汰该页 

4.cloak算法的分析与改造(最近没有使用)

(1)如果缺页很少,会所有R=1:记录了太长的历史信息

  • hand scan(指针)一圈后,淘汰当前页,将调入页插入hand位置,hand前移1位,退化为FIFO 
  • 解决方法:定时清除R位
  • 再来一个扫描指针,且该指针移动速度快
  • 清除R位指针移动速度 > 用来选择淘汰页移动速度

5.给进程分配多少页框

  • 分配多:请求调页导致的内存高效利用就没用了
  • 分配少:系统出现颠簸/抖动(CPU利用率急剧下降的现象)

  • Belady现象:对有的页面置换算法,页错误率可能会随着分配帧数增加而增加
    • FIFO会产生Belady异常。
    • 栈式算法无Belady异常,LRU,LFU(最不经常使用),OPT都属于栈式算法

实际中,OS根据实际情况来调整页框数

操作系统梳理: 

swap in / swap out -----> 虚拟内存 -----> 段页内存管理 -----> 程序载入 -----> 进程(多进程图像)​​​​​​​

猜你喜欢

转载自blog.csdn.net/weixin_45864705/article/details/127893376