《现代操作系统》03章 存储管理(一)
又是周末到来时,学习成果分享之!
0 前导
帕金森定律:不管存储器有多大,程序都可以把它填满(我的理解:即使你的手机存储空间有128G甚至256G,你也会把它装满或是出现运行卡顿,有些信息的使用频率过低但任然会占用存储资源)
程序员的梦想:廉价、私有、无限、高速、永久(掉电不丢失)的存储器
但愿程序员的梦想能由你我来实现!
现实的存储体系:一个分层存储的抽象模型,这个模型在第一章第三节中略微提及,传送门
这个抽象模型需要管理,操作系统中的存储管理器(memory manager)可以胜任,它记录、分配、回收内存资源。
注意:最底层的高速缓存由硬件完成,本章将由简入繁的介绍针对编程人员的内存模型、及内存*的优化管理、磁盘的管理为下一章内容*
1 无存储器抽象
最简单的存储器抽象就是根本没有抽象,这样程序直接访问物理内存,例如汇编语言MOV R1,1000
的意思是将地址1000处的内容移动到Register1中。(如果学过简单的单片机汇编语言应该很容易理解)
无存储器抽象操作系统的三种主要结构:
这种无存储抽象的设定导致同一时刻内存中仅能存在一个进程
第一种方案现在很少使用,用户程序可能损毁系统
第二种在掌机和嵌入式系统中应用
第三种方案用于早起PC(例如MS-DOS),ROM中的驱动程序称为BIOS,用户程序可能摧毁系统
无存储抽象运行多道程序
以IBM360早期模型举例
内存划分为2KB的块,每个块拥有四位保护键(存储在CPU特殊寄存器中)
PSW中有一个四位码(表示当前允许访问的块,只有操作系统可以修改),当进程将要访问的块与PSW不符时,操作系统会进行相应处理,避免进程、操作系统间的干扰
由于程序员编写进程使用的绝对地址,导致多个进程被装载后会出现不可避免的访问越界,这需要一个装载器,它的任务是在装载进程时要将进程中有关地址访问的绝对地址+偏移地址,例如存储在磁盘中的进程有一个跳转语句JMP 28
,而该进程被装填在地址16384处,则需要将该语句修改为JMP 16412
在大型机、小型机、台式机、笔记本中已经看不到这种构造的内存抽象,但是在简单地嵌入式系统(微波炉、冰箱、智能卡等)中还存在着他们的身影。操作系统可以以库的形式实现,例如ecos系统
2 地址空间
无存储抽象的系统将物理地址直接暴露给进程,(单CPU时)很难实现要求多进程快速响应的应用环境,并且还可能出现应用程序破坏系统的情况。
2.1 何为地址空间
地址空间为程序提供一种抽象内存,地址空间是一个进程可用于寻址内存的一套地址集合。在每个程序的地址空间内,程序员不用再担心两个进程访问相同的地址时会发生冲突
【例如两个进程中都有JMP 28
,在运行时会被映射到真实物理内存(如16384)而未必是28】
地址空间的概念在其他领域同样存在,例如IP地址也有地址空间(IPV4:0~2*32-1)、网站的域名也是一个地址空间(数字字母下划线+.com/cn/org/edu等等)
基址寄存器与界址寄存器
这种方法曾经常见,随着CPU的发展和抽象技术的迭代,这种方法不再使用了,但我们还是要学习一下其中的思想。
一种简单地动态重定位
给CPU配置两个特殊的硬件寄存器(一般只有操作系统有权限对寄存器值进行修改):基址寄存器、界址寄存器,
程序的运行将按照以下机制执行:
缺点:每次的内存访问都要进行一次比较和加法运算,比较速度较快,但加法(由于进位)若没有特殊的运算电路仍然会耗费时间而拖慢运行速度
2.2 交换技术
内存交换系统的大致流程:
空闲区(hole):也叫空洞,内存中空闲的空间
内存紧缩(memory compaction):向下移动进程将分散的Hole合并成更大的Hole,消耗CPU时间多,一般不使用
内存该分配多少?
有些进程的大小是固定的,直接装入即可,有些进程会存在动态变量(从堆向下增长动态的分配内存,在栈向上增长存放返回地址和局部变量),这时需要给装入内存的进程分配一些额外的预留空间。
注意:当进程换出到磁盘时按照实际使用内存的大小交换,这要不会浪费磁盘空间。
如果预留空间使用完,此时内存中有足够的Hole,将其交换到更大的Hole;若没有足够的Hole则交出内存,回到磁盘等待足够的Hole,或者被终止。
2.3 空闲内存管理
老王今有农田128亩(内存128K),有租户(进程)来租地,怎们把地分给他们呢。老王找计算机系毕业的儿子出主意。
2.3.1 位图与存储管理
小王立刻想到了学过的位图,跑出去买了一袋石灰粉,用石灰粉按照2亩一格(分配单位)地把128亩地分成了64份;他给每格地编了号并在他的电脑里新建了一张Excel表格(位图)存放每格地的使用情况
Excel中1表示占用0表示空闲
分配单元的大小影响内存管理的效率:
大单元 | 小单元 |
---|---|
位图小,占用空间小 | 位图大,占用空间大 |
产生较大空间浪费(一格未占满) | 产生较小的浪费 |
位图的搜索效率很低:加载新的进程需要在位图中搜索连续的0串,而有些0串跨越了字节(例如 11111000 00000111),效率大大降低.
2.3.1 使用链表的存储管理
有新的租户来找老王租地,老王要凑在电脑上找半天有没有足够大的连续的地租给租户,看的是头晕眼花,赶紧又把小王叫过来让他改进改进。
小王琢磨了一会,想到了一种学过的数据结构——链表,顿时心生一计,抄起电脑就是干。
链表是一种利用指针连接的数据结构,就像一根链条,每个链节都可存放一些数据或指针,链表的查找、创建和删除都非常的迅速,因此效率较高。
P代表进程 H代表空闲区,第一位数字代表起始编号,第二位数字代表该区域的长度
使用双向链表可以在进程退出时更方便的进行相邻Hole的合并
这样一来,老王只要搜索有没有大小适合的空闲节点,按照节点信息寻找对应的起始地块即可,然后根据情况修改链表中的信息。
首次试配算法(first fit)
已知进程要使用的内存大小,从表头顺序搜索链表,找到足够长的空闲区;若空闲区长度等于需求,将节点修改为进程占用状态;若空闲区大于需求,将空闲区分为两块,一块存放进程,一块继续作为空闲区,同时增加一个进程占用节点,并修改空闲区节点的起始编号和长度
下次试配算法(next fit)
在这次搜索完成后记录位置,下次从该位置开始搜索,其他同首次试配算法。性能略低于首次试配算法
最佳试配算法(best fit)
从头到尾搜索一遍,找到适合将要换入进程的最小空闲区。
缺点:搜索速度慢,浪费内存比首次试配或下次适配还严重(因为最佳适配产生了许多小的无法使用的空闲区)
加一点改进:将进程链表和空闲区列表分离,在寻找空闲区时不用经过进程占用节点,搜索速度大大提高。并且对空闲区按大小进行排列,再次提高搜索速度。(此时使用首次试配与最佳适配速度相同,下次试配无意义)
缺点:两张表在其中之一改变时需要完成同步,延长了进程释放内存的时间
该算法著名并广泛使用
快速试配算法(quick fit)
为常用大小的空闲区维护专用链表
21K的空闲区可以就近放在20K下,也可以单独开一个链表存放,这就看如何决策了
由此来快速匹配合适的空闲区
X 往期文章
Python+OpenCV+imutils的简单图片处理(放缩、翻转、旋转、灰度RGB提取)
如果文中有误,还请在评论区指正。这里是海小皮,我们一同进步!!!