目录
存储器是计算机系统的重要组成部分之一。对存储器加以有效管理,不仅直接影响存储器的利用率,而且对系统性能有重大影响。存储器管理的主要对象是内存,对外存的管理在文件管理中
一、程序的装入和链接
在多道程序环境下,要使程序运行,必须为之先建立进程。创建进程的第一件事是将程序和数据装入内存。
将用户源程序变为可在内存中执行的程序的步骤:
①编译:由编译程序将用户源代码编译成若干个目标模块
②链接:由链接程序将编译后形成行程的一组目标模块,以及它们所需要的库函数链接在一起,形成一个完整的装入模块
③装入:由装入程序将装入模块装入内存【之后等待cpu调度执行】
1.程序的装入
将一个装入模块装入内存时,有三种方式:
- 绝对装入方式
- 可重定位装入方式
- 动态运行时装入方式【理解即可】
(1)绝对装入方式
在编译时,如果知道程序驻留在内存的什么位置,那么编译程序将产生绝对地址的目标代码
装入模块装入内存后,程序中的逻辑地址与实际内存地址完全相同,不须对程序和数据的地址进行修改
- 程序中所使用的绝对地址(物理地址),可在编译或汇编时给出,也可由程序员赋予
- 只适合于单道程序环境
内存分为os区和用户区,单道程序装入时(内存中没有其他程序),所以物理地址唯一确定,直接装到用户区的0号地址;如果内存有其他程序,逻辑地址都是0,但用户区0号地址只有一个,所以物理地址不确定
(2)可重定位装入方式
在多道程序环境下,目标模块的起始地址通常从0开始,程序中的其他地址都是相对于起始地址计算的。因此应采用可重定位装入方式,根据内存的当前情况,将装入模块装入到内存的适当位置。【内存哪里有空闲,就把程序安装到哪个位置】
装入时,逻辑地址向物理地址的转换叫做重定位
在装入时对目标程序中指令和数据的修改过程称为重定位。地址变换在装入时一次完成,以后不再改变,称为静态重定位
如果上图作业在一段时间之内暂时不执行或者永远不执行了,就会占据内存宝贵的资源,需要把这个作业调出的外存,等内存空间充足了或想要执行这段作业了再把他调回来,就一定还要调回到10000-15000这个位置,因为内存装了很多作业,这个位置可能被其他程序占用了,就无法调进来了,出现错误。
为此,引入动态运行时装入方式
(3)动态运行时装入方式
动态运行时的装入程序,在把装入模块装入内存后,并不立即把装入模块中的相对地址转换为绝对地址,而是把这种地址转换推迟到程序真正要执行时才进行
- 装入内存后的所有地址都仍是相对地址
- 为使地址转换不影响指令的执行速度,应设置一个重定位寄存器
①动态重定位的实现
在动态运行时装入的方式,将相对地址转换为物理地址的工作在程序指令真正要执行时才进行。地址转换需要重定位寄存器的支持。程序执行时访问的内存地址是相对地址与重定位寄存器中的地址相加而成
2.程序的链接
程序经过编译后得到一组目标模块,再利用链接程序将目标模块链接,形成装入模块。根据链接时间的不同,把链接分成3种:
- 静态链接:在程序运行前,将目标模块及所需的库函数链接成一个完整的装配模块,以后不再拆开
- 装入时动态链接:指将用户源程序编译后所得的一组目标模块,在装入内存时,采用边装入边链接的链接方式
- 运行时动态链接:指对某些目标模块的链接,是在程序执行中需要该目标模块时,才对它进行链接
(1)静态链接方式
事先进行链接,以后不再拆开
将目标模块装配成装入模块时需解决的两个问题:①对相对地址进行修改;②变换外部调用符号
(2)装入时动态链接
用户源程序经编译后所得的目标模块,是在装入内存时,边装入边链接的,即在装入一个目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将它装入内存,还要修改目标模块中的相对地址。【等待真正要装入内存时,才链接】
优点:
- 便于修改和更新【各个模块分开存放,需要执行哪个模块,就找到哪个模块,进行链接】
- 便于实现对目标模块的共享【各个模块分开所以便于共享】
(3)运行时动态链接
运行时动态链接是将对某些模块的链接推迟到执行时才执行,即在执行过程中,当发现一个被调用模块尚未装入内存时,立即由os去找到该模块并将之装入内存,把它链接到调用者模块上。
凡执行过程中未被用到的目标模块,不会调入内存和链接,这样不仅加快程序的装入过程,而且节省大量的内存空间【因为有些目标模块压根不需要被执行,也放到内存中,就浪费了内存的资源】
二、连续分配方式
连续分配方式,指为一个用户程序分配一个连续的内存空间,是早期的内存分配方式
- 单一连续分配
- 固定分区分配
- 动态分区分配
- 可重定位分区分配
1.单一连续分配
把内存分为系统区和用户区两部分,系统区仅提供给os使用,通常放在内存地址部分,用户区是指除系统区以外的全部内存空间,提供给用户使用。
但只能用于单用户、单任务的操作系统中,也就是说整个用户区只能分配给一个用户程序使用,不管这个用户区域有多大的空间
2.固定分区分配
(1)原理
将内存用户空间划分为若干个固定大小的区域,在每个分区中只装入一道作业,便可以有很多道作业并发执行。当有一空闲分区时,便可以再从外存的后备作业队列中,选择一个适当大小的作业装入该分区,当该作业结束时,可再从后备队列中找出另一作业调入该分区
(2)划分分区的方法
①分区大小相等
所有的内存分区大小相等,缺乏灵活性
②分区大小不等
把内存区划分成含有多个较小的分区、适量的中等分区及少量的大分区
(3)实现
为便于内存分配,通常将分区按大小进行排队,并为之建立一张分区使用表,其中包括每个分区的起始地址、大小及状态(是否已分配)。当有一用户程序要装入时,由内存分配程序检索该表,从中找出一个能满足要求的、尚未分配的分区,将之分配给该程序,然后将该表项中的状态置为”已分配“;若未找到大小足够的分区,则拒绝为该用户程序分配内存
3.动态分区分配★
动态分区分配指根据作业的大小分配内存,分区的大小和个数依据装入作业的需要而定
在实现过程中涉及如下问题:
- 分区分配中的数据结构
- 分区分配算法★
- 分区分配及回收操作
(1)分区分配中的数据结构
①空闲分区表示
- 空闲分区表:记录每个空闲分区的情况。每个空闲分区占一个表目
- 空闲分区链:在每个分区的起始部分,设置一些用于控制分区分配的信息,以及用于链接各分区所用的前向指针;在分区尾部则设置一后向指针,将所有的空闲分区链接成一个双向链【由前向指针和后向指针链接成一个链】
②已占分区说明表
结构:作业号;起始地址;大小
(2)分区分配算法★
为把一个新作业装入内存,需按照一定的分配算法,从空闲分区表或空闲分区链中选出一分区分配给该作业
常用的分配算法:
- 首次适应算法
- 循环首次适应算法
- 最佳适应算法
- 最坏适应算法
- 快速适应算法
①首次适应算法FF(First Fat)
FF算法要求空闲分区表以地址递增的次序排列。在分配内存时,从表首开始顺序查找,直至找到一个大小能满足要求的空闲分区为止;然后按照作业的大小,从该分区中划出一块内存空间分配给请求者,余下的空闲分区仍留在空闲分区表中。若从头到尾不存在满足要求的分区,则分配失败
- 优点:优先利用内存地址部分的内存空间,保留了高址部分的大空闲区
- 缺点:低址部分不断划分,产生小碎片;每次查找从低址部分开始,增加了查找的开销【比如完成一次作业的分配后,又来了一个作业,要从表首开始查找】
②循环首次适应算法
再分配内存空间时,从上次找到的空闲分区的下一个空闲分区开始查找,直到找到一个能满足要求的空闲分区,从中划出一块与请求大小相等的内存空间分配给作业
为实现算法,需要:设置起始查寻指针;采用循环查找方式
- 优点:使内存空闲分区分布均匀,减少查找的开销
- 缺点:缺乏大的空闲分区
③最佳适应算法
所谓“最佳”是指每次为作业分配内存时,总是把能满足要求、又是最小的空闲分区分配给作业,避免“大材小用”。要求将所有的空闲分区按其容量以从小到大的顺序形成一空闲分区链
- 缺点:产生许多难以利用的小空闲区(越接近作业大小,产生的碎片越小)
④最坏适应算法
最坏适应分配算法要扫描整个空闲分区表或链表,总是挑选一个最大空闲区分割给作业使用。该算法将所有的空闲分区按其容量以大到小的顺序形成一空闲分区链,查找时只要看第一个分区能否满足作业要求
- 优点:剩下的空闲区还可以利用,同时查找效率很高【19k的作业分配20k的空闲,剩余1k不能利用了,但分配空间大剩余分区还可以利用】
- 缺点:缺乏大的空闲分区
(3)分区分配及回收操作
①分配内存
利用某种分配算法,从空闲分区链(表)中找到所需大小的分区。设请求的分区大小为u.size,表中的每个空闲分区的大小表示为m.size,若m.size-u.size≤size(size是规定的不再切割的分区大小),将整个分区分配给请求者,否则从分区中按请求的大小划出一块内存空间分配出去,余下部分留在空闲链中,将分配去首址返回给调用者
②回收内存
当进程运行完毕释放内存时,系统根据回收区首址,在空闲分区链(表)中找到相应插入点,可能有4种情况:
- 回收区与插入点的前一个分区F1邻接【A:直接修改F1的大小(加上回收区的大小)】
- 回收区与插入点的后一个分区F2邻接【B:更改F2的起始地址和F2的大小(加上回收区大小)】
- 回收区与插入点的前后两个分区F1、F2邻接【C:F1、R和F2形成一个更大的空闲分区,F2在空闲分区表中有一条记录,需要删除这条记录】
- 回收区既不与F1邻接,又不与F2邻接【D:在空闲分区表中建立一个新的表项,填写回收区的起始地址和大小,或者插入到空闲分区链中的适当位置】
4.可重定位分区分配
(1)动态重定位的引入
在连续分配方式中,必须把系统或用户程序装入一连续的内存空间。如果在系统中只有若干个小分区,即使它们的容量总和大于要装入的程序,但由于这些分区不相邻,也无法将程序装入内存
解决方法:将内存中的所有作业进行移动,使它们全部邻接,这样把原来分散的小分区拼接成大分区,这种方法称为“拼接”或“紧凑”
缺点:用户程序在内存中的地址发生变化,必须重定位【经过紧凑拼接之后的用户程序在内存中的位置发生了变化,如果不对程序的地址加以修改,很难保证作业3和作业4的正常执行】
(2)动态重定位的实现
在动态运行时装入的方式,将相对地址转换为物理地址的工作在程序指令真正要执行时才进行。地址转换需要重定位寄存器的支持。程序执行时访问的内存地址是相对地址与重定位寄存器中的地址相加而成。
(3)动态重定位分区分配算法
动态重定位分区分配算法与动态分区分配算法基本相同,差别在于增加了紧凑的功能。
5.对换
(1)对换的引入
- 阻塞进程占据大量内存空间
- 许多作业在外存而不能进入内存运行
对换:把内存中暂时不能运行的进程或暂时不用的程序和数据,调到外存上,以便腾出足够的内存空间,再把已具备运行条件的进程和进程所需要的程序和数据调入内存【类似挂起和激活】
对换的分类:
- 整体对换(或进程对换):以整个进程为单位
- 页面对换或分段对换:以页或段为单位
实现进程对换,系统必须具备的功能:
- 对换空间的管理
- 进程的换出
- 进程的换入
(2)对换空间的管理
一般从磁盘上划出一块空间作为对换区使用
磁盘分为文件区和对换区
存储内容 | 驻留时间 | 主要目标 | 分配方式 | |
文件区 | 文件 | 较长久 | 提高文件存储空间的利用率 | 离散 |
对换区 | 从内存换出的进程 | 短暂 | 提高进程换入和换出的速度 | 连续 |
在系统中设置相应的数据结构以记录外存的使用情况
对换空间的分配与回收 ,与动态分区方式时的内存分配与回收雷同
(3)进程的换出与换入
- 进程的换出 系统首先选择处于阻塞状态且优先级最低的进程作为换出进程,然后启动盘块,将该进程的程序和数据传送到磁盘的对换区上
- 进程的换入 系统应定时查看所有进程的状态,从中找出“就绪”状态但已换出的进程,将换出进程最久的进程作为换入进程,将之换入,直至已无可换入的进程或无可换出的进程为止