cpu执行程序的基本过程
- 译码器 输入为n管脚,输出为2^n根管脚,编号为从0到2^(n-1),用少的输入端控制更多的输出端
- 最常用的是三八译码器
- AD(Address bus)地址总线: 选中一行数据
- 每一行 8bit 组成8吧B cpu输入端32根线,输出端就可以控制 2^32 ,因此可以控制4G内存
- DB(Data bus)数据总线: 8根数据线组成数据总线,确定了每一列,通过行和列将1B数据数据通过数据总线传输到cpu内部的寄存器里面,使用R表示寄存器
- 程序最终是一条一条被读入寄存器内执行的(有限 ,且按照功能进行分类,有通用寄存器和特殊寄存器,特殊寄存器只能被特殊的指令访问,)用户态(访问通用寄存器) <=> 内核态(访问特殊寄存器和通用寄存器)
- 内存条是一个临时的保存中介
- 磁盘是一个永久的保存中介
- 地址总线的选中原理 (译码器原理)
- 了解四大类存储器的速度和所处的位置 容量大小
- 寄存器 (cpu内部)> cache(cpu内部) > 内存卡 (cpu外部)> 磁盘(cpu外部)
- 寄存器只会保存数据;cache也需要将数据拷贝到寄存器里面执行
- ALU 算数逻辑运算单元 负责运算
- 举一个例子 计算机计算 3+2 计算器读取3 将3读到R1寄存器,然后读+,ALU会读+,因为+需要两个数,因此通过地址总线形成地址,将2读到R2寄存器,计算,通过R1和R2取数据,然后将数据计算结果存到寄存器R3,然后找地址,将数据结果存储到内存的一个位置,(写回内存)。
CPU位数 OS位数 内存地址总线数 内存数据总线数 逻辑地址位数 物理地址位数
- cpu位数 和 寄存器的位数相关,如果寄存器的个数是32则cpu是32位;如果寄存器个数是64则cpu是64位。
- 32位的寄存器可以处理2^32大小的数据;64位的寄存器可以处理2^64大小的数据
- os位数(操作系统的位数):硬件限制软件;软件可以自定义(软件分成32位或者64位);os位数等价于逻辑地址的位数,二者相等。也等价于虚拟内存的理论大小。
- 32位cpu只可以安装32位操作系统;64位cpu既可以安装32位也可以安装64位操作系统
- 数据总线数 如果是8根 如果传输32位数据,需要传输4次;64位 传输数据 需要8次;现实生活中有 8 、16 、32 和 64根数据总线
- 数据总线:一般考研常规使用的是8根数据总线 ,每次可以传输8位数据,如果寄存器的大小是32,需要传输4次将数据填满一个寄存器;如果是64位寄存器,需要传输8次将数据填满一个寄存器
- 可访问内存的大小 + 磁盘大小 大于等于 虚拟内存的实际大小
- 虚拟内存理论大小 大于等于 虚拟内存实际大小
- 但是现实生活中有 16位 32位 64位数据总线
- 物理地址总线数 (物理地址总线数):比如上面的例子中,地址总线有32位,每一个地址是由32位0或者1组成,从硬件限制可访问内存的大小。这里属于1,硬件限制,限制了可访问内存的大小。比如电脑是32位的,那么根据可以访问的物理地址总线,有0-2^32-1,也就是4G的内存,即使安装8G的内存条,剩余的空间是无法访问的。
- 或者2,内存卡限制,比如32位机器安装2G内存卡,剩余的空间无法访问
- 或者3,软件限制,比如译码器原理,限制输入端输入的位数,限制输出端可以访问的地址
- 三个限制 ,取最小值作为可访问的内存大小
- 逻辑地址 是将程序从磁盘拷贝到内存,可以拷贝最大的行数,这个和操作系统的位数有很大的关系
内存管理逻辑图
- 程序经过编译和链接形成exe程序,从磁盘将程序装入内存执行,进行程序的运行
- 装入?1,如何装入?2,装在哪里?(1)记录 (表格) (2) 查询 (表格)
编译和链接
- 编译,将C语言转化为汇编语言,机器语言
- 各个模块的逻辑地址,逻辑地址也叫做相对地址
- 链接 ,将多个有交集的汇编语言、机器语言合并到一起,会改变先前的逻辑地址,经过编译链接之后形成可执行文件
- LOAD 1,6 将第六行的变量加到寄存器1里面;ADD 1,8 将第8行变量和寄存器1里面的数据相加;STORE 1,10 将计算的结果(寄存器1中的数据)存储到第10行变量C
- 汇编 每一行指令占据两行,汇编使用逻辑地址指向变量
装入
- 如果随意装入程序,cpu可能无法找到每一条执行程序。因为编译的代码所使用的的指令是相对本程序而言的,比如 LOAD 1,6;是将第六行的变量放入到寄存器1里面,而cpu会将整个内存条的起始位置作为程序的开始位置,因此,对这个命令使用的位置6和整个内存条的位置6是冲突的,形成地址错位。
- 地址错位 本质上是由于 逻辑地址和物理地址不匹配 造成的冲突;
- 物理地址是绝对的;逻辑地址是相对的
- 将程序从磁盘 拷贝到 内存 ,其逻辑地址会发生变化,指令后面所使用的相对地址也要随之变化,才可以运行。
- 程序编译使用的是逻辑地址(相对地址);内存卡使用的是物理地址(绝对地址)。
- OS位数和逻辑地址的位数 是等价的。如果程序编译很长,超出位数的代码无法识别,不可以超过机器程序(2^操作系统的位数)的大小。
- C语言中 函数名字和变量名字本质上都是地址
- 解决方案 :1,绝对装入;2,静态重定位装入;3,动态重定位装入
解决虚拟地址和物理地址错位的三种方法
1,绝对装入
- 装入前就确定好程序的装入位置,使得程序逻辑地址和物理地址对齐,不错位
- 开发程序的时候就指定我要从 指定的位置上编译程序,比如我的程序执行需要将程序拷贝到内存的第1000行以后,这样内存地址和物理地址一一对齐,不会错位
- 那么这个程序每次运行永远拷贝到指定的位置
2,静态重定位装入
- 装入时候,动态指定将程序装入的位置,由装入程序的逻辑地址进行一次性修改,从而避免错位。每拷贝一行就改变一行中的地址,从而避免错位(边装入边修改)
- 如果不指定,都是默认从0开始作为起始地址
- 那么每次执行的程序的逻辑地址都是不同的,这个地址是装入前动态设置的,因此是 重定位 的概念
- 静态是指,这个程序一旦加载到内存,地址就指定了,就不可以改变了;要想移动,只可以关闭程序,重新指定程序的起始位置
3,动态重定位装入
- 程序运行的时候,利用重定位寄存器来弥补作用,让cpu认为逻辑地址和物理地址是对齐的,不错位
- 将程序原封不动拷贝到内存,此时逻辑地址和物理地址是存在一个错位。程序拷贝到内存会创建一个pcb,存储相关信息,其中入口地址,指定程序拷贝到内存的位置。
- 利用CPU内部的重定位寄存器,存储上述提到的内存位置。每次运行的时候,会在指令的后面 异步加上内存的位置,补齐逻辑地址和物理地址之间的错位。
- 如果程序变化,程序直接移动,直接修改pcb和重定位寄存器里面数值就可以让逻辑地址和程序地址对齐
内存保护
- 也叫做越界保护
- 内存保护 由 硬件和os操作系统共同保障
- 实现方法:
- 1,上下限寄存器,比如木马程序拷贝到内存中的1000到2000行,那么下限是1000,上限是2000,如果内部程序想访问3500,和上下限对比,不符合,因此报错
- 2,基址寄存器(重定位) + 限长寄存器(界地址);告诉首地址,通过限长寄存器得到程序的占用空间,因此得到程序的上限
单一连续内存分配
- 内存卡分为系统区(os操作系统)和用户区(操作软件)
- 用户区每次只可以装入一个程序
- 特点:
- 1,单用户 单任务
- 2,内存卡利用率极低 因为有内部碎片,程序只用到了一部分
- 3,通常采用绝对装入的方式
固定分区分配
- 装入多道程序
- 克服单一连续分配只可以装入一个程序的缺点,在用户区进行划分,每个区块分配不同的程序
- 划分可以都相等 或者 每个区块的大小不一样,减少浪费
- 特点:
- 1,用户区分成很多小的分区,每个分区只装入一道程序
- 2,分区的大小很有讲究,太小装不进程序;太大内存利用率低
- 3,有内部碎片(内部碎片:内存分给你了,你没有合理使用内存,就叫内部碎片)
- *外部碎片:内存空间除去分给程序的各个分区之后,剩余的空间不足以放下任何程序,这个剩余的空间不属于任何程序,叫做外部碎片
- 需要分区说明表 记录分区号 分区的大小 起始位置 状态
- 提交程序的时候,查看哪一块内存是空闲的
- 一般使用静态重定位技术
动态程序分配
- 解决固定固定分区提前设定分区大小的弊端,进程程序要多少给你多少,不多分配不少分配。
- 如果用户区间只剩下2MB不足以分配程序,那么需要等待先前程序释放资源,比如qq退出,释放了8MB的内存空间,lol立刻占用这8MB中的6MB,剩余2MB,这2MB就是外部碎片
- 由于程序的进进出出,导致的碎片数量越来越多,空间越来越小。进行碎片合并。将很多碎片合并在一起,形成一个大的区间,这个合并过程是一个随机的事件
- 需要使用紧凑的技术,较少外部碎片,碎片的 移动 合并,这个移动就涉及到了动态重定位
- 动态分区 需要分区说明表,每一块分区的大小不确定,动态变化
- 如果一个程序加入内存,但是有很多区块都可以存放数据,那么这个这个程序如何存放呢??
- 动态分区算法:
- 1,首次适应:从内存区间头部开始,第一次可以存放的位置
- 2,最佳适应:从内存区间头部开始,找一个尽可能不浪费的区块;但这个并不是最佳的,反而会产生更多小的碎片,不可用,只能使用紧凑技术合并更多的碎片
- 3,最坏适应:从内存区间头部开始,找一个最大的区块;产生的碎片不会很小,反而可以给其他程序使用,因此不一定是最坏适应
- 4,邻边适应:每次找合适的区块不是从头开始,而是从上一次的位置开始往下找
覆盖技术
- 软件运行有128kb,要在64kb的内存上运行,覆盖技术要求程序员在设计程序的时候,设计每个程序的启动的先后顺序,将内部分区分为固定区,存储主程序;覆盖区用于代码之间的运行、覆盖。
- 特色:
- 1,用在同一个程序进程中
- 2,覆盖技术实现了小内存运行大程序,但是这不是万能的
- 3,对程序员要求高 ,考虑性能
交换技术
- 交换技术发生在内存紧张的情况下
- 交换技术主要用于不同进程(程序)之间
- 覆盖技术已经过时,交换技术仍然存在
- 将处于阻塞状态下的程序拷贝到交换区域,将需要运行的程序拷贝进入内存区,交换是指内存和磁盘之间的交换
分页
基本分页存储
- 先前使用的固定分区分配产生内部碎片,动态分区分配会产生外部碎片。
- 每个碎片可能很小,但是积少成多,总和是一个不小的浪费
- 分散分配:将大的程序进行拆分变成很多的小的碎片,每个碎片分别拷贝到内部或者外部产生的碎片中。
- 但是以什么作为标准进行切分呢??大小不等,切片很难。因此引入分页思想,都切成一样大小的
- 特色:
- 1,程序可以被切块
- 2,内存也可以被切块
- 3,切块越小,浪费越小
- 因为碎片的大小是随机产生的,因此对程序的切片不好处理,思路很简单,实现很难
如何分页
- 将内存卡(物理地址)和软件程序(逻辑地址)都等分成4kb,然后将软件程序的拷贝到内存卡上面执行,程序的大小和物理内存的分块大小都一致,但是如果不可以整除,会有部分浪费,但是这种浪费很小。
- 内存、磁盘、程序都会被切块,内存卡的一块叫做 页框(页帧)
- 程序的片段叫做页面,页面可以被装入页框中
- 磁盘的分块叫做 磁盘块
- 磁盘 程序 和 内存 都按照4kb进行切割,这样程序导入 磁盘和内存都很方便;切片的大小需要符合2的指数,2^0 = 1; 2^2 = 4; 2^4 = 16;
- 为什么分块使用4kb??这个不确定,有1Kb、16kb和32kb,4kb最常用,都是2的n次方的整数倍
- 设置mmu,就可以实现对内存、程序和磁盘的kb大小的选择,切换1kb、4kb和16kb
页表
- 内存卡的分块编号,从0开始排序,其编号也叫做物理块号、页框号、页帧号
- 程序 切分,也会进行编号,从0开始,其编号叫做页号、页面号
- 将内存卡切分的编号和程序切分的编号对应起来,叫做页表。这个页表存储在内存中,每个进程都有自己的页表。创建进程的同时会创建pcb,pcb会存储这个页表的信息,从而让操作系统找到这个页表
- 页表有俩列,好多行,第一列存储页号,存储逻辑地址;第二列存储物理块号,也是物理地址
- 页表项 是 页表的一行
- 页表 通过pcb被查询到
- 页表只能记录块和块之间的映射关系,4kb 可以存储很多的代码,因此不会得到每一行代码在哪一块中的哪一行等相关信息
页表、物理地址、逻辑地址三者之间的关系
- 数学关系
- 二进制向左移1位表示十进制除以2
- 人类视角 十进制 整除取整为页号;求余为业内偏移
- 机器视角 二进制 高位为页号;低位为业内偏移
- 32位 以4k作为一个页,4k = 2^12 因此,低12位作为页内偏移,12到32位作为页号;读高20位得到哪一页(页号),读低12位知道属于哪一行(业内偏移)
- 通过逻辑地址 -> 页表(页号) -> 通过页表里面的逻辑地址和物理地址的对应关系,找到物理块号,将物理块号 和 业内偏移合并在一起就得到了物理地址
- 逻辑地址和物理地址里面的业内偏移 是一一对应的,因为把程序切片拷贝到内存,代码里面的地址是相对的,起始到终止的差距就是业内偏移。物理块号代表这个物理块开始的地址,加上这个代码的业内偏移,也就是物理块结束的地址。
- 编号为何从0开始?整除取整直接得到页号
基本分页 基本地址变换机构
- 对逻辑地址进行上面的操作,就可以得到页号,通过页号查询页表,查询到某一页程序被装载到内存中哪一个物理块里面,查询到物理块之后和业内偏移量拼接在一起,构成一个物理地址
- 上面这些需要使用到地址变换机构,才可以得到业内偏移等信息
- 逻辑地址 前20位表示页号,后12位表示业内偏移量。 通过页号 和 匹配的物理块号对应,
- 页表存储在内存里边,页表记录的是页号和物理块号(页框号)的对应信息
- 页表寄存器,存储在cpu里面,前半段存储页表的起始地址,后半段存储存储页表长度(程序放到内存里边执行的时候最大的长度)。这个是唯一的,一级页表,后面会有多级页表,动态通过pcb进行更新
- 页表寄存器 相当于给定指针 + 数据长度,得到的这一块内存就是存储的页表信息
- 页表长度 存储的是所有页号的信息
- 页表长度 * 4KB = 程序的大小
- 页号 大于 页表长度,说明这个程序不属于当前程序,产生 越界中断。如果小于 则属于合法的,就可以进行查表,CPU先根据页表寄存器里边存储的页表起始地址找到 页表开始的地方,根据页表存储的页号,在页表里面查询。通过页号找到后面存储的页框号(物理块号),将页框号(物理块号) 和 业内偏移量拼接在一起,就形成了物理地址
- pcb更新页表寄存器的数值
考点
- 执行一条指令一共会访问几次内存?
- 执行指令,需要物理地址,1,寻找物理地址,2,读入cpu内部,3,执行指令
- 1,寻找物理地址:利用页号到内存里边的页表查询物理块号,拼接形成物理地址,一次访问内存
- 2,读入cpu内部,找到物理地址需要将指令拷贝到CPU的内部,一次访问内存 然后执行命令,一级页表的前提下,需要访问两次内存
- 如果是两条指令 则是4次
具有快表的地址变换机构
- 快表也是页号和物理块号的拼接,相对存储在内存中的页表,快表存储在页表寄存器,是比页表先一步被访问。主要的目的是记录先前曾经访问过的历史记录,类似于电脑的快捷访问,为了减少访问内存的次数,但是存储的条数很少。因为存储在寄存器,因此CPU先调用寄存器很快,如果找不到才会访问内存。
- 因为快表会保存最新的访问记录,是一个动态更新的过程,因此在访问内部指令的时候有可能会查到有可能查不到,有一定的概率。这个叫做命中率,局部性原理 时间 + 空间
- 问题:执行1条指令,命中率是90%,那么需要访问几次内存?如果快表存在 1次;如果快表不存在需要两次;乘上对应的概率 总的访问内存次数 = 0.9 * 1 + 2 * 0.1 = 1.1
- 因此:执行10条指令,命中率是90%,那么需要访问几次内存? 10 * 1.1 = 11次,相较于没有快表,2 * 10 = 20次
- 快表 命中率很高 是因为程序的局部性原理,也就是for等循环,在这个地方执行很多次,局部原理 体现在时间和空间局部性
两级页表
- 引入两级页表的原因
- 一级页表 页号 + 物理块号 组成的页表项,一共有n个页表项
- 一个页表项有多大?估计
- 估计的逻辑:1,逻辑地址 (程序最大空间) -> 2,页面大小 (视情况而定,4kb、16kb) 3,做多分几页;4,页号位数;5,估计页表大小项 补充
- 页表项确定之后,一个页表项有多大 ,因为一个页表很大,如果多个页表进入内存,会很卡,考虑到分页的时候,程序和内存都可以被分块,页表是不是也可以分块??
- 发明一个页表,记录页表的切块,变成了两级页表的概念
如何设计两级页表
- 条件:32位操作系统 4kb页面大小 4B页表项大小
- 设计
- 1,按照最大程序进行切割
- 2,页表切块,块大小 = 页面大小
- 3,当一个页表可以装在一个页面之内时,多级页表就设计结束
- 4,逻辑地址切块,块位数 = 该级页表每块容纳的页表项
如何设计多级页表
- 条件:64位操作系统 4kb页面大小 4B页表项大小
- 设计
- 1,按照最大程序进行切割
- 2,页表切块,块大小 = 页面大小
- 3,当一个页表可以装在一个页面之内时,多级页表就设计结束
- 4,逻辑地址切块,块位数 = 该级页表每块容纳的页表项
虚拟内存
引入请求分页的原因
- 将QQ程序拷贝到磁盘,由于文件管理,将qq程序在磁盘上进行存储。运行的时候,将QQ程序从磁盘拷贝到内存,QQ程序的登录界面只会执行一次,体现了一次性,如果他只执行一次却一直留在内存会导致内存的浪费,驻留性。因此 需要优化一次性和驻留性,
- 优化的依据,程序的局部性原理,程序中有太多的循环,太多的模块
- 如何优化,按照需要分批装入、调出 -> 请求分页存储管理 (虚拟内存)
- 请求分页管理的操作流程 -> 运行程序之前,为程序分配小于整体大小的内存空间,比如先装入登录模块的程序,登录完成之后,将登录模块的程序调出,将需要的程序按照需要逐个装入。
请求分页的工作原理
- 在内存的一个地方存储一个QQ程序的页表,查询页表实现逻辑地址和物理地址的转换,从而得到程序的片段存储到物理磁盘的哪一个块里面,以及存储在物理块里面的哪一行
- 在cpu的内部存储一个快表,快表是程序的页表历史记录的备份。因为基本分页,页表是存储的QQ程序切割之后存储到内存中物理块的对应的逻辑信息
- 请求分页的工作原理:1,大致的流程一致,但是页表记录的信息不一样。因为按照需求,需要将文件切片动态的植入换出,这就需要记录哪些页面已经在内存中,页面执行的次数等信息,因此引出了(置换算法),动态将需要的页面放入内存中,替代先前的使用过的,不需要的页面
- 基本分页页表 记录了每一块的逻辑地址和物理地址的对应关系;但是 对请求分页页表而言,有的时候找不到,这个块还没有装载到内存中,需要将这个块替换到内存中分配的驻留集里面来使用。
- 相较于基本分页,请求分页对逻辑地址和物理地址的对应关系虽然也需要通过页表,但是并不简简单单,期间多了mmu,虚拟地址到物理地址翻译
助留集 和 工作集是一个意思
- 二者是一个概念,就是操作系统给单个进程分配的几个物理框所装的页面的集合
- 每次程序运行的时候需要将程序从磁盘拷贝到内存,根据生存时间的不同分为主程序页面 (常驻内存的页面) 和 子程序页面
- 比如 主程序界面作为一个界面UI,这个常驻内存,除非程序退出或者异常退出;而不同的功能就是子程序,需要的时候调入内存执行,使用完之后调出内存
- 驻留集的大小:不能过大,如果过大,退化为基本页表,浪费内存;如果过小,置换页面发生频繁,运行程序的时候会很卡;要适中,相对的概念
三种策略 分配助留集的大小的策略
- 固定分配局部置换:计划经济策略,非常死板,计划赶不上变化,程序是变化的;局部是指按照需求,所需的只可以替换自己事先分配好的页面
- 可变分配全局替换:低级的市场经济策略,盲目扩大生产,产业泡沫;如果程序需要更大的内存空间,会进一步动态增加驻留集的大小,但是会导致装入程序的数量下降,先前分配给程序的驻留集不会减少;
- 可变分配局部替换:高级的市场经济策略,抽肥补瘦,动态调整,十分灵活;减少缺页率。每个程序所需要的驻留集的大小是动态调整的
- 缺页率 :当分配的物理块(驻留集)一定的时候,调入和调出的频率是成正比
- 可变分配局部替换在n1 n2之间动态变化,协调缺页率和物理块数量之间的关系
页表改进 和 缺页中断
- 基本分页是将程序所有的内容拷贝到内存中;请求分页是按照需要将需要的装入内存,动态装载调出
- 请求分页页表 具体列(字段)如下
- 页号 0 - n
- 物理块号
- 状态位 有效位,标志是否在内存中,否则缺页中断,并且进入阻塞状态;如果物理块有数据,但是状态为0,表示并没有被装入,状态位控制物理块是否有效;有效的位数要小于驻留集的大小;如果产生缺页中断,利用调度算法从磁盘将需要的页拷贝到内存
- 外存地址 磁盘块号,如果需要的页不在内存,需要从磁盘块导入页
- 为置换算法提供参考使用的参数 访问字段(统计使用,区分程序是长期使用还是短期使用,执行次数越少的越容易被调出)、修改位(对于变量的数值修改需要从内存改回磁盘)、使用位(clock算法使用)等
- 如果需要的页不在内存中 就会产生缺页中断,然后从磁盘里面取页面到内存中,替换
页面调度时机
- 程序开始运行的时候,使用预调页策略(局部性原理),程序员指定的,main函数,操作系统底层将其作为程序的入口
- 程序的运行过程中 修改请求页表相关信息
- 程序运行中,发生缺页时,比如程序启用不同的功能;调入一页页面,请求调页策略,(页面置换算法),使用硬件中断,中断处理程序,启用页面置换算法,将新的页面替换旧的页面
置换算法
OPT页面置换算法 最佳 向未来看
- 算法思想:淘汰以后永不访问 或者 将来最长时间不再访问的页面
- 算法特点:不可以预测未来,因此这个算法不能实现
- 推断出 缺页中断次数 / 页面置换次数
- 缺页次数 - 页面置换 = 工作集(驻留集)
- 理想化,很难实现,因此其余算法是不可能超过他的
- 发生缺页中断的时候 除了刚开始没有数据,剩余的都会发生页面置换
- 下面的例子 发生了9次缺页中断
FIFO页面置换算法
- 算法思想:淘汰先调入的页面,队列实现
- 算法特点:简单、性能差、且有belady异常
- 推断出 缺页中断次数/页面置换次数
- 15次中断 15-3 = 12次 页面置换
LRU算法 最近未使用算法 向历史看
- 算法思想:淘汰最近未使用的页面 (使用过去来预测未来)
- 算法特点:性能优异,接近最佳置换算法,需要硬件栈的支持,开销极大
- 推断出 缺页中断次数 / 页面置换次数
CLOCK算法 又叫NRU(Not recently used)
- 算法思想:通过钟表扫描法,淘汰最近未使用的页面(页表增加一个使用位)首次装入置1;再次访问置1;扫描时将1变为0
- 算法特点:由于最近未使用和最久未使用的思想接近,因此CLOCK 算法和LRU算法性能很接近
- 推断出 缺页中断次数 / 页面置换次数
- 但是CLOCK算法性能更高,不需要硬件,而LRU算法需要硬件栈的支持
改进CLOCK算法
- 算法思想:通过钟表扫描法,淘汰最近未使用的页面中未修改页面
- (u,m) use modify
- (0,0) 未使用未修改
- (0,1) 未使用已修改
- (1,0) 已使用未修改
- (1,1) 已使用已修改
- 算法特点:相比CLOCK算法,减少了页面回写磁盘的概率,从而省却了回写的时间 (I/O时间)
- 对变量的数值修改会导致内存和磁盘数据的不一致,因此要根据修改的内容更改磁盘数据;先淘汰未使用的,就减少更新数据所带来的IO操作
- 算法步骤:1,按照钟表扫描法,寻找(0,0)用于替换,找不到就进行第二步;2,重新执行钟表扫描算法,寻找(0,1)用于替换,在扫描中,将(1,0)改为(0,0),(1,1)改为(0,1),找不到回到第1步
- 注意事项:第1步只查不修改;第二步边查边修改
- 改进CLOCK算法更加细腻
从何处调入页面,调出的页面存放在哪里?
- 临时存放程序的副本 -> 磁盘必须要有一块对换区(swap)
- 可修改程序 -> 变量 (已修改 / 未修改);可修改程序如果已经修改,回写磁盘,写到对换区里面的程序副本里面
- 不可修改程序 常量
- 如果对换区很大的时候,每次运行的时候,将程序拷贝到对换区,程序运行的时候,是将对换区里面的程序拷贝到内存
- 对换区大小问题(运行程序,将程序从文件夹拷贝一份到磁盘的对换区,然后从兑换区拷贝程序到内存区)
- 对换区足够大,从磁盘的对换区将数据文件拷贝到内存
- 对换区不够大,对换区只装入可修改程序,不装入不修改程序,不可修改程序从程序安装文件夹读取
- unix 方式:读取文件都是从程序安装文件里面,但是回调的时候,无论是可修改程序还是不可修改程序都写到对换区里面;然后第二次以后就会从对换区进行数据交互
- 1,从何处调入页面?从磁盘的对换区将数据文件拷贝到内存
- 2,调出的页面存放在哪里?将修改的内容回写到对换区,不可修改程序不会回写到文件夹
- 不对源文件进行操作,防止不同用户之间的数据干扰
- 程序安装目录是原件,对换区拷贝的是程序的副本
虚拟内存的大小
- 虚拟内存 小于等于 逻辑地址支持的最大空间(软件限制) 软件支持的容量很大,不需要考虑,一般硬件限制就是虚拟内存可以支持的最大内存
- 磁盘装载大程序的一部分 + 内存装载大程序的一部分,合起来使大程序在小内存内运行,通过对换区,实现数据的交互
- 64位OS -> 2^32 * 4GB
- 32位OS -> 4GB
- 虚拟内存 小于等于 (内存卡 + 磁盘)硬件限制
- 用小内存运行大程序
- 考题形式:在一台实现了虚拟内存技术的计算机里面,最大支持运行多大的软件
- 游戏由程序、数据、模型(人物)、过渡动画等组成
抖动现象
- 发生时间:如果驻留集不够大的话,置换页面时就可能会产生抖动现象(对换区 和 内存区 之间交换数据),又叫颠簸
- 特点:1,频率高;2,来来回回
虚拟地址和物理地址之间的翻译
CPU执行一条指令的过程
- 通过地址翻译,由虚拟地址得到物理地址(先查快表,再查页表)
- 通过物理地址,将指令读到CPU内的寄存器(ALU)里面执行
- 页表 :页号、物理块号、有效位 三个最关键
- 快表查询:直接映射(Hash查找)、全相联映射、组相联映射
- 全相联映射:快表和页表一样,查询的时候需要从头查到尾
- 因为 全相联映射 内部存储的条目是无序的,查询比较浪费时间,因此改进为组相联映射
- 2路组相联映射,是指将先前的两个条目组合在一起,形成一组,相较于先前的全相联映射多了组索引、TLB索引,他俩是通过 页号 进行分解得到的。
- 虚拟地址(逻辑地址结构):将先前全相联映射中的页号p化为TLB标记和组索引、业内偏移w不变;
- 如果是8条目2路组相联,分成四组,将页号的后两位(00,01,10,11表示组号)作为组索引,剩余的作为TLB,在组索引唯一的条件下,TLB是唯一的标识
- 只有地位作为组索引,高位仍然满足递增序列,且唯一
虚拟内存-虚拟地址到物理地址的翻译 例题
系统满足如下条件,
- 有一个TLB与一个datacahe
- 存储器以字节为单位进行编址 ;以字节为单位进行编址,一行8bit作为一个地址,一行作为0;两个字节为单位进行编址,将先前的两行编为0;全字,四个字节编址
- 虚拟地址14位
- 物理地址12位
- 页面大小64B
- TLB四路组相联,共16个条目
- data cache是物理地址,直接映射,行大小为4字节,总共16组
- 请翻译虚拟地址
- 0x03d4 ;
- 0x00f1 ;
- 0x0229;
- 有快表,先到快表里面查询,将虚拟地址按照快表的格式进行切分
基本分段管理
- 基本分页是将程序所有文件以4kb作为一个切块,存储在磁盘的一段空间;基本分段是按照功能或者模块将相关联的代码作为一个整体,分散存储在磁盘中
- 分段的好处:程序多开时可以共享数据;分段可以实现程序多开,或者多个程序公用一个程序段
- 比如qq程序开了两个,登录不同的qq号,但是有些功能是共享的,比如网络,因此将网络的代码进行复用
如何进行程序分段
- 按照程序(进程)自然段进行划分,这个流程是由编译器决定的
- 段长:每段段长都可能不一样,和程序本身的结构相关
- 分页有页表,记录程序转入到内存的位置,那么分段也应该有段表,功能一致,记录
- 页表:记录程序的哪一页被记录到内存中的哪一块
- 段表:记录程序的哪一段被记录到内存中的哪一块
段表
- 基本分段的段表,每个进程都有自己的段表
- 段号、段长、内存起始地址(因为段长不一样,不可以向页表一样,使用物理块号(等分),因为每一块大小都是4kb,因此可以通过 物理块号 * 4kb 计算得到物理地址)
- 通过段表 共享数据
- 分页存储管理通过查询快表和页表,将逻辑地址翻译为物理地址,从而cpu可以到具体的位置执行指令
物理地址、逻辑地址和段表之间的关系
- 段号、段内偏移
- 段内偏移w:由于每一个程序段的大小不一样,以最大的作为段内偏移
- 到段表查询,得到物理的起始地址b,则物理地址等于b + w
- 低16位表示段内偏移 请翻译逻辑地址 0x000301F4
分段地址变换机构
- 计算机步骤
- 1,根据逻辑地址的前几位得到段号,如果段号大于段表存储的最大长度(段表长度),则产生越界中断,否则合法
- 2,如果合法 通过起始位置 + 段号 * 段表项长度 = 段表项地址 得到段表项地址b
- 3,通过 E=b+w 段表项地址 + 段内偏移 = 物理地址
- cpu执行一条指令 需要访问几次内存?两次 1,地址翻译,需要查询段表,得到物理地址;2,通过物理地址到内存读取cpu指令到cpu内部执行
分页和分段的地址空间维度
- 分页的地址空间是一维的,因为只要给定页面大小这一个参数就可以划分逻辑地址的结构,比如每个大小都是4kb,(4kb = 2^12B)因此业内偏移是12位,页号占了20位
- 分段的地址空间是二维的,不可以通过段长计算段号和段内偏移的位数,因为每段的长度都不一样,因此需要给出这两个参数
基本段页式管理(综合段式 和 页式)
引入段页式原因
- 页式存储管理:通过程序与内存的切成小块,分散和分配内存,减少内存碎片,提高内存利用率。机器的角度
- 段式存储管理:通过将程序先按照自然段(模块)进行分段,达到通用段可以共享的目的。从人类的角度出发
- 段页式存储管理:先将程序按照自然段进行分段,再将每个自然段切成等大的页。从而汇聚两者的优势
如何分段和如何分页
- 先分段 再分页
- 分段,由编译器完成,将自然段进行分段
- 分页,将分号的段按照4kb进行切分,每4kb作为一个页,不足4kb的碎片,会出现不足一页的情况 ,这样的内部碎片很小且很少
- 因为分成段的数量就很少,而且仅仅在最后的时候会产生碎片,因此碎片很小且很少
段表和页表
- 段表和页表配合使用之后,先前的段表记录段号、段长和段起始地址变成了,段号、段长(页表长度)和页表起始地址
- 因为对每个段进行分页,因此每个段都有自己的页表起始地址
- 只有一个段表,段表中的每个段表项都有自己对应的页表
- 还实现不了逻辑地址和物理地址的转换,需要更进一步操作
物理地址、逻辑地址、段表、页表之间的关系
- 页号p 业内偏移w : 分页的逻辑结构 -> 查询页表 得到物理地址 物理块号b b|w
- 段号s 段内偏移w : 分段的逻辑结构 -> 查询段表 得到物理地址 物理块号b b+w
- 段号s 页号p 业内偏移w : 段页式逻辑结构 -> 先查段表再查页表(因为先分段再分页)
段页式地址变换机构
- 将逻辑地址变换成物理地址
- 1,取出段号S和段号表存储的段号比较,如果大于等于最大段表长度,则报越界中断错误;如果小于等于段表长度,查询页表;通过起始地址 + 段号 * 段表项长度 计算得到页表
- 页表的计算方式一样,得到物理块号
- 物理块号b 和 段内偏移w 得到物理地址 b|w
- cpu执行一条指令需要执行几次内存?一共需要三次
- 1,地址翻译,需要查询段表,查询页表,得到物理地址;两次
- 2,通过物理地址到内存读取cpu指令到cpu内部执行 一次