内存的基础知识
内存分为按字节编址(8位)和字编制(不同计算机不一样,64位计算机就是64位,即8个字节)
相对地址=逻辑地址 绝对地址=物理地址
从逻辑地址到物理地址的转换由装入解决。
装入的三种方式
- 绝对装入:在编译时知道程序放在内存中的哪个位置,编译程序将产生绝对地址的目标代码。
灵活性很低,只适用于单道程序环境,只有单道程序环境可以在程序运行之前就能确定程序将要放入哪个位置 - 静态重定位:又称为可重定位装入。装入模块全部使用逻辑地址,在装入的时候将逻辑地址全部转换为物理地址。
特点是必须分配其要求的全部内存空间,如果没有足够的内存,就不能装入作业。作业一旦进入内存后,在运行期间就不能再移动,也不能再申请内存空间。 - 动态重定位:又称为动态运行时装入。装入模块使用相对地址。相对地址到物理地址的转变要等到实际运行的时候才能确定。因此装入内存后所有的地址仍然是逻辑地址。这种方式需要一个重定位寄存器的支持。
重定位寄存器:存放装入模块存放的起始位置。当实际运行的时候实时地将逻辑地址转换为物理地址。
采用动态重定位允许程序在内存中发生移动。可以将程序分配到不连续的存储区:在程序运行前只需要装入他的部分代码即可投入运算,然后在程序运行期间根据需要动态申请分配内存,便于程序段的共享,可以向用户提供一个比存储空间大得多的地址空间。
链接的三种方式
- 静态链接:在程序运行之前,先将各个目标模块以及他们所需要的库函数连接成一个完整的可执行文件(装入模块),之后不再拆开。
- 装入时动态链接:将各目标模块装入内存时,边装入边链接
- 运行时动态链接:在程序执行中需要该目标模块时才对他进行链接,优点是便于修改和更新,便于实现对目标模块的共享。
内存管理需要解决的问题
- 内存空间的分配与回收
- 操作系统需要提供某种技术从逻辑上对内存空间进行扩充
- 地址转换(三种装入方式:绝对装入、可重定位装入/静态重定位、动态运行时装入/动态重定位)
- 内存保护:保护各个进行运行互不干扰
方法一:在CPU中设置一对上下限寄存器,存放进程的上下限地址。进程的指令要访问某个地址时,CPU检查是否越界。
方法二:采用重定位寄存器(基址寄存器)和界地址寄存器(限长寄存器)进行越界检查。重定位寄存器中存放的是进程的起始物理地址,界地址寄存器中存放的是进程的最大逻辑地址。
内存空间的扩充
覆盖技术
用于解决“程序大小超过物理内存总和”的问题
覆盖技术的思想:将程序分为多个段(多个模块)。常用的段常驻内存,不常用的段在需要时调入内存。
内存中分为固定区 和 若干个覆盖区
需要常驻内存的段放入固定区中,调入后就不再调出(除非运行结束)。不常用的段就放在覆盖区中。按照自身逻辑结构,让那些不可能同时被访问的程序段共享同一个覆盖区。
程序员必须声明覆盖结构,操作系统完成自动覆盖。缺点:对用户不透明,增加了用户编程负担。(已经没人用了)
交换技术
也叫做对换技术:内存空间紧张时,系统将内存中某些进程暂时换到外存中,把外存中某些已经具备运行条件的进程换入内存(中级调度)
进程的PCB需要常驻内存。
中级调度(内存调度):决定将哪个处于挂起状态的进程重新调入内存。
暂时换出外存等待的进程的状态为挂起状态。
挂起状态可以分为:就绪挂起、阻塞挂起两种状态。
-
应该在外存的什么位置保存被换出的进程?
具有对换功能的操作系统中,通常把磁盘文件分为文件区和对换区两部分。文件区主要用于存放文件,主要追求存储空间的利用率,因此对文件区空间的管理采用离散分配方式;对换区空间只占磁盘空间的一小部分,被换出的进程数据就存放在对换区。对于对换的速度直接影响到系统的整体速度,因此对换区空间的管理主要追求换入换出速度,因此通常对换区采用连续分配方式。总之,对换区的I/O速度比文件区的更快。 -
应该什么时候交换?
交换通常在许多进程运行且内存吃紧时进行,而系统负荷降低就暂停。例如:在发现许多进程运行时经常发生缺页,就说明内存紧张,因此可以换出一些进程;如果缺页率明显下降,就可以暂停换出。 -
应该换出哪些进程?
可优先换出阻塞进程;可换出优先级低的进程;为了防止优先级低的进程饥饿,有的系统还会考虑进程在内存的驻留时间。。。(PCB常驻内存)
覆盖技术和交换技术的区别:
- 覆盖是同一个程序或者进程中
- 交换是在不同进程/作业中的
内存分配
连续分配管理
系统为用户进程分配必须是一个连续的内存空间。
单一连续分配
内存被分为系统区和用户区。内存中只能有一道用户程序,用户程序独占整个用户区空间。
优点:实现简单,没有外部碎片;可以采用覆盖技术扩充内存;不一定需要采取内存保护(eg:早期的MS-DOS)
缺点:只能用于单用户、单任务的操作系统;有内部碎片;存储器利用率极低
内部碎片:分配给进程的内存区域中,如果有些部分没有用上,就是内部碎片。
固定分区分配
将用户区划分为若干个固定大小的分区,在每个分区中之状如一道作业,这样就形成了最早的,最简单的一种可运行多道程序内存管理方式。
如果分区大小相等:缺乏灵活性,但是很适用用于同一台计算机控制多个相同对象。
如果分区大小不等:增加了灵活性,可以满足不同大小进程的需求。产生一些大的,产生一些小的。
分区说明表:来实现每个分区的分配与回收。每个表象对应一个分区,通常按分区大小分配。
优点:实现简单,无外部碎片
缺点:当用户程序太大时,可能所有分区都不能满足需求,用覆盖技术来解决可能降低性能;可能产生内部碎片,内存利用率低
动态分区分配
又称为可变分区分配。根据进程装入内存时的大小动态建立分区。因此系统分区的大小和数目时可以改变的。
- 操作使用什么样的数据结构记录内存
- 空闲分区表:每个空闲分区对应一个表项
- 空闲分区链:每个分区的起始部分和末尾部分分别设置前向指针和后向指针。起始部分处还可以记录分区大小等信息。
- 当很多空闲分区都能够满足需求时选择哪一个
使用动态分区分配算法
- 分区的分配和回收
动态分区没有内部碎片,但是有外部碎片。
内部碎片:分配给某进程的内部区域中如果有些部分没有用上
外部碎片:内存中的某些空闲分区由于太小而难以利用
为了解决外部碎片,我们可以采用紧凑技术,通过将不同进程的内存空间挪挪位将不连续的内存空间凑在一起。为了实现这种技术我们应该是用动态重定位的装入方式,每次紧凑之后都将进程的起始地址都放在基址寄存器(重定位寄存器)中。
动态分区分配算法
首次适应算法
每次从低地址开始查找,找到第一个能够满足大小的空闲分区
实现:空闲分区以地址递增的次序排列。每次分配内存时顺序查找空闲分区表(链),找到第一个满足的空闲分区。
最佳适应算法
尽可能留下大片的空闲区,优先使用更小的空闲区
实现:空闲分区按照容量递增的次序链接,每次分配内存时找到第一个满足的空闲分区。每次分配以后还需要对空闲分区排序使其按照容量大小递增。
缺点:每次采用最小的分区选择,会产生很多外部碎片
最坏适应算法
每次分配优先使用最大的连续空闲区,这样每次分配以后空闲区就不会太小。
实现:空闲分区按照容量递减链接。每次分配使用第一个空闲分区(如果第一个都不满足就GG了)然后对空闲分区进行排序
缺点:如果大进程到达就会导致没有空间使用
邻近适应算法
首次适应算法每次从开始查找导致开始部分出现很多外部碎片。为了解决这个问题,每次从上次查找结束的位置开始检索。
实现:空闲分区以地址递增的顺序排列成循环链表。每次内存分配从上次查找结束的位置开始查找。同首次适应算法一样每次分配以后不需要对链表重新排列。
首次适用算法虽然容易产生外部碎片,但是可能保留大分区。邻近适应算法虽然减少产生外部碎片,但是可能没有外部碎片。
综合来看首次适应算法还是比较优秀的。