操作系统学习笔记3(内存管理)
1.存储(Memory)
-
-
存储单元是连续的地址空间
2.存储管理的任务
-
存储分配和释放
-
地址重新分配
-
存储的分享和保护
-
存储的扩展
3.无存储器抽象
-
每个程序都能够看到物理内存
-
在内存中同时运行两个程序是不可能的
4.地址空间(Address Space)
-
为了能够使多个程序同时在内存中并且能够互不干扰,有两个问题必须要被解决
1.保护
2.地址的重定位
-
地址空间是一些能够让进程去寻找内存的地址的集合
-
每一个进程有它自己的地址空间,并且独立于其它进程的地址空间
-
地址能够分为逻辑地址(Logical address)和虚拟地址(Virtual address)
-
动态重定位
把进程的地址空间映射到物理内存的不同部分
-
可以给每个CPU配置两个特殊的寄存器,分别是基址寄存器(Base Register)和界限寄存器(Limit Register)
-
这两个寄存器能够给每个进程一个独立的地址空间
5.交换(Swapping)
-
交换技术是把一个进程完整的调入内存,使该进程运行一段时间,然后把它存回磁盘
-
虚拟内存能够让程序在只有一部分被调入内存的情况下执行
-
交换技术允许很多内存共享一个部分
-
大小增长的内存可以被换出之后换入到更大的内存之中
-
随着进程的换入和换出内存中会产生空洞(hole)
-
可以将这些小的内存合成一大块,成为内存紧缩(memory compacting)
6.空闲内存管理
1.使用位图(bitmap)的空闲内存管理
-
每一个分配单元对应位图上的一位
-
0代表空闲,1代表已经分配过
-
分配单元越小,位图越大
-
但是在一个新的进程到来时,必须要找到一串连续的0才能够分配,而寻找的时间开销太大了
2.使用链表的存储管理
-
用链表来分配和释放内存段
-
链表的每一个结点包括空闲域(H)和进程(P)的指示标志,起始地址,长度和指向下一节点的指针
-
-
段链表按照地址排序有下列优点:
-
当进程终止或被换出时链表的更新非常直接
3.存储置换算法
1.首次适配算法(First Fit)
-
从头开始找到了第一个足够大的空闲区就可以放入
-
产生平均大小的空洞
2.下次适配算法(Next Fit)
-
从上次结束的位置开始搜索,找到一个足够大的空闲区就可以放入
-
性能比首次适配算法稍差一些
3.最佳适配算法(Best Fit)
-
遍历整个列表,找到能够放入进程的最小的空闲区
-
它会产生大量无用的小空闲区
4.最差适配算法(Worst Fit)
-
总是分配最大的可用空闲区,使新的空闲区比较大从而能够继续使用
-
这会导致运行大的程序比较困难
5.快速适配算法(Quick Fit)
-
为常用大小的空闲区维护单独的列表
-
寻找空闲区是十分快速的,但是当进程换入换出时,合并过程是非常费时的
7.虚拟内存(Virtual Memory)
1.覆盖(Overlaying)
-
把程序分割成很多片段
-
但是这些工作必须交给程序员来做
-
把这些工作交给计算机来做就是虚拟内存(Virtual Memory)
2.虚拟内存
-
把用户的逻辑内存和实际的物理地址分离开来
-
为用户提供尽可能大的虚拟内存
-
逻辑地址空间能比物理地址空间更大
-
在磁盘中存储虚拟内存
-
在程序执行的过程中,只需要有一部分的程序需要在内存中
-
能够让地址空间被多个程序分享
-
加载和存储缓存好的虚拟内存不需要用户程序的干预
3.分页(Paging)
-
分页是用来实现虚拟内存的一个技术
-
把虚拟的内存分为许多单元,每一个单元称为页(Page)
-
在物理内存中对应的单元称为页框(Page frames)
-
内存管理单元(MMU)是可以把虚拟地址转化为物理地址
-
当MMU注意到页面没有被映射,使CPU陷入操作系统,这个陷阱称为缺页中断(page fault)
-
页表(page table)可以给出虚拟地址和物理地址之间的关系
-
虚拟地址可以分为两个部分,虚拟页号(高位)和偏移量(低位)
-
页表的目的就是把虚拟页映射到页框中
-
虚拟地址的位数规则,假设逻辑地址有n位,每一个页面的大小为m位,则偏移量就是m位,虚拟页号就是n-m位
-
页表面对着两个主要的问题
映射的速度必须要快
页表有可能很大
-
转换监视缓冲区(Translation Look-aside Buffers TLBs)
-
TLB也被称为快表或相联存储器
-
TLB表中一般有:虚拟页号,物理页号,有效位,访问位
-
硬件控制TLB
-
一、TLB miss
1.硬件加载PTE(page table entry)到TLB中
2.如果PTE是无效的就产生一个错误
3.虚拟内存软件进行错误处理
4.重启CPU
-
二、TLB hit,使用硬件检查有效位
1.如果有效,就指向内存中的页框
2.如果无效,就由硬件产生页错误,之后进行故障处理和重启故障指令
-
软件控制TLB
-
在TLB miss的时候,CPU陷入到操作系统中
1.检查包含PTE的页是否在内存中
2.如果没有,就执行页面故障处理
3.如果没有空闲条目,就回写,然后将PTE加载到TLB中
4.重启故障指令
hit的情况跟硬件一样
-
多级页表
-
当页表太大时,我们可以采用多级页表
-
我们可以把虚拟页号位分为:对二级页表的的页表索引和第二级页表中的页。
-
倒排表(Invert page table)
-
实际内存中每个页框对应一个表项,而不是虚拟页面对应一个表项
-
对虚拟页面进行散列计算,并且作为索引
4.页面置换
1.获取策略(Fetch Strategies)
-
什么时候把页面从磁盘存储区转为主内存
2.更换策略(Replacement Strategies)
-
当引入其它页面并且没有足够的空间,主存储中的哪个页面将从主存储中删除
3.清理策略(Clean Strategies)
-
什么时候页面从内存中取出
4.取出的过程
-
除非需要,不会把页面放入主内存的算法
1.页面错误发生
2.检查虚拟内存地址是否有效,如果没有,就杀死这个进程
3.如果引用是有效的,就检查它是否已经缓存到内存中了
4.找到一个空闲的页面框架
5.将地址映射到磁盘块,获取磁盘块到页帧,暂停用户进程
6.磁盘读取完成后,为页框添加虚拟内存映射
7.如果有必要,重新启动进程
5.预分页
-
使一个页面提前成为主页面的算法
6.页面置换
-
找到磁盘上这个页的位置
-
找到一个空闲的页框
-
-
如果有空闲的页框,就使用
-
如果没有,就采用页面置换算法选择一个页框
-
如果需要,就将所选页写入磁盘,并更新任何必要的表
-
-
从磁盘中读取所需要的页面
-
重启用户进程
5.页面置换算法
-
最优策略(OPT)
替换将来最远时间内不会再次使用的页面
-
随机页面置换算法
随机选择一个页面
-
FIFO First in First Out
置换一个已经在内存中待了最长时间的算法
-
NRU Not Recently Used
-
LRU Least Recently Used
替换掉没有使用时间最长的页面
-
NFU Not Frequently Used
替换掉最不常用的页面
-
工作集置换算法
-
在内存中保留正在积极使用的那些页
1.OPT
-
假设每一页都可以用在该页面首次被访问之前所要执行的指令数作为标记
-
这种方法是不切实际的,因为它需要未来的数据
2.FIFO
-
维护所有页面的一个链表
-
按照顺序,当它们进入内存时,最老的页面在链表的最前面
-
优点:这种方式容易实现
-
缺点:在内存中时间最长的有可能是经常被使用的,会被抛弃掉
-
Belady发现更多的页框并不代表就能有更少的页错误
-
第二次机会页面置换算法
-
添加一位R,初始为0,如果这个页面在置换前被用过,那么R置为1,当需要置换页面时,先检查R位,如果R位为1,则将其置为0,并且放到链表的末端,修改装入时间。
-
时钟页面置换算法
-
核心是第二次机会页面置换算法,不过把页面排成了一个环,就像时钟一样
-
发生缺页中断时,如果R位为0,则淘汰页面,如果R位为1,就清除R位并向前移动表针
3.NRU
-
NRU是最近未使用页面算法
-
我们让每个页面有访问位(reference bit R)和修改位(Modified bit M)
-
当进程开启时,所有页面的R和M位都被置为0
-
R位会被周期性的清零
-
每个页会被标记
-
第0类:没有被访问,没有被修改,R=0,M=0
-
第1类:没有被访问,被修改,R=0,M=1
-
第2类:被访问,没有被修改,R=1,M=0
-
第3类:被访问,被修改,R=1,M=1
-
NRU从类别最小的页面里随机移除一个
4.LRU
-
LRU是最近最少页面置换算法
-
LRU换出没有被使用时间最长的页面
-
LRU用软件来实现
最经常使用的放在前面,最不常用的放在后面
但是每次访问都要更新这个列表,开销太大了
-
LRU硬件实现方法1
使用一个64位的计数器
它在每条指令之后递增
在内存访问之后,计数器的值存储在刚刚引用的页的页表条目中
每次换出时选择计数器的值最小的页面
计数器需要周期性的归零
-
LRU硬件实现方法2
-
对于一个有n个页帧的机器维护一个n\timesn的矩阵
-
当每次有一个页帧被访问时
-
把K所在的这一行全部置为1
-
之后把K所在的这一列全部置为0
-
在这个矩阵里,二进制值最小的行即为LRU需要换出的页面
5.NFU
-
NFU是最不常用页面置换算法
-
该算法的每个页面都与一个计数器相关联,计数器的初始值为0,每次时钟中断时,扫描所有的页面,将该页面的R位加到它的计数器上
-
发生缺页中断时,置换计数器最小的页面
-
NFU存在的问题是NFU不会忘记,可能在很久之前引用频繁的页面有着很高的计数
-
NFU算法的一种改进,称为老化算法(aging)
-
老化算法就是在R位被加进去之前先把计数器右移一位,并且R位加到计数器的最左端
6.工作集页面置换算法
-
工作集就是k个最近的内存引用使用的页面集
-
w(k,t)就是在t时刻最近k次所访问过的页面
-
随着内存访问的局部性区域的位置大致稳定时,工作集大小也大致稳定
-
当局部性区域的位置改变时,工作集快速扩张和收缩过渡到下一个稳定值
-
在工作集页面置换算法中每个页面有上次使用时间和访问位R,有一个生存时间的参数t,页面的生存时间等于当前实际时间减去上次使用时间
-
在每次缺页中断时,扫描所有的页面,如果所需要的页面在工作集中,就把R位置为1,如果不在,那么对于工作集里的每一个页面,如果R=1,那么设置上次使用时间为当前实际时间,如果R=0且生存时间大于t,则移出这个页面,如果R=0且生存时间小于等于t,就记录生存时间最长的页面
-
因为每次中断发生都要扫描整个页面,所以开销比较大,一种改进就是工作集时钟算法
-
在每次缺页中断时,首先检查指针指向的页面,如果R位为1,那么就把R位清零,指向下一个页面
6.分页系统中的设计问题
-
页面置换有局部页面置换和全局页面置换
-
负载控制
-
如果工作集超出了内存容量,就容易发生颠簸(thrash)
-
解决方法就是暂时的从内存中去掉一些进程
7.页面大小
-
小页面的优点,有更少的内部碎片,内存中未使用的程序较少
-
小页面的缺点,程序需要更多的页,同时页表也会跟更大
-
最优页面大小的公式为P=\sqrt{(2se)} ,s为进程平均大小,e为每个页表项需要的字节
-
我们可以把指令空间和数据空间分离开来
-
清除策略,在分页系统中有分页守护进程(paging deamon),定期地检查内存的状态,如果有很少的页面是空闲的,那么就根据页面置换算法来移除页面
8.有关实现的问题
-
在包含分页的操作系统中会遇到以下的问题
-
1.进程创建
-
-
确定程序的大小
-
确定页表
-
-
2.进程执行
-
-
对于新进程,重新设置MMU
-
刷新TLB
-
-
3.页错误
-
-
确定造成错误的虚拟内存地址
-
对页面进行页面换入换出操作
-
-
4.进程退出
-
-
释放页表和页
-
1.缺页中断的处理
-
硬件陷入内核,在堆栈中保存程序计数器
-
保存通用寄存器
-
OS确定需要那个虚拟页面
-
OS检查这个地址是否有效,寻找空闲页框
-
如果选择的页框是一个脏页框,那么就把它写入到磁盘中,中止这个进程
-
OS从磁盘引入调度新页面,当页面加载时,进程仍然处于中止的状态
-
当磁盘中断发生时,说明该页已经装入,页表可以进行更新
-
恢复发生缺页中断指令以前的状态,程序计数器重新指向这条指令
-
调度发生缺页中断的进程
-
继续执行程序
9.分段(Segmentation)
-
对于某些应用程序,拥有两个或多个虚拟地址空间可能比只有一个要好得多
-
分段是一种支持内存用户视图的内存管理方案
-
页面的大小是固定的,但是段的大小是可变的
-
程序是段的集合,段是一个逻辑单元
-
逻辑地址可以包含两个部分,段号和偏移量
-
段表:将用户定义的二维地址映射为一维物理地址,每个表项有基址(Base)和界限(Limit)
-
基址包含了段驻留在内存中的起始物理地址
-
界限确定了段的长度
-
段表基址寄存器(STBR)指向段表在内存中的哪个位置
-
分段可以使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护
-
但是使用分段程序员必须知道所使用的内存模型
-
分段会浪费一定的内存
-
段有可能太大而放不到物理内存中
1.分段和分页结合
-
每一个进程都需要段表
-
段表里的每一项都指向该段的页表