存储管理

 

存储管理

在操作系统中,负责管理存储器层次结构的程序称为存储管理器

 

4.1 基本的存储管理

4.1.1 单道程序存储管理

 

对于A图,操作系统位于主存最底部的RAM,即随机存取存储器中,用户程序位于主存的上部。
对于B图,操作系统位于主存最高端的只读存储器里(ROM),(其实本身属于一种映像区域,映像了主板上的基本的输入输出系统)。
对于C图,设备的驱动程序位于内存最高端的ROM中,操作系统的其余部分位于低端的RAM中,中间是用户的应用程序。如MS-DOS系统。
对于IBM操作系统,系统位于ROM中的部分即为BIOS。

 

4.1.2 固定分区的多道程序系统

对于常见的系统,我们希望能够支持更多的程序,当某个程序处于等待I/O的时候,可以让CPU为别的程序服务,来提高系统整体的性能。
因此可以用划分分区的方法来同时加载多个程序,可以把主存分为几个大小不同的分区,根据程序的不同,来把他们加载到不同的分区中。而同时,程序也可按照单个输入队列的方式进行输入,也可以按照多个输入队列的方式进行输入。

对于左图,由于作业的大小类似的时候,而且众多的作业大小都类似,而此时分区的大小固定,我们该选择合适大小的分区来运行这个作业,这样的话,类似的作业就被分到了一起,而那些与作业的大小不一致的分区,就必然会出现空闲状态,那些分到一起的进程却不得不等待,等待要分到内存空间。忙得忙,闲的闲。

对于右图,所有的作业都被放入一个队列,每次当有分区被释放的时候,就从等待队列中寻找最适合这个分区大小的进程,这样做是为了避免把大的分区分给那小的作业,免得资源被浪费,但这样却常常把小的作业推到了后面;但对于操作系统,我们做普通的要求就是简单的操作能被最快的反馈给我们,即小的作业能够被最快的处理,因此,为了总能够满足用户的这个要求,一般要专门保证总有一个小的分区,专门来处理那些小的事件、作业,免得被大的作业抢去分区。
或者通过加权来判断作业被推后的次数,到了一定的程度,就不再跳过而是立即执行。

 

4.1.3 重定位和存储保护

多道程序导致了分区的产生,而分区也导致了新的问题,即程序的重新定位和保护。

原因: 当程序装入内存时, 操作系统要为该程序分配一个合适的内存空间,由于程序的逻辑地址与分配到内存物理地址不一致, 而CPU执行指令时,是按物理地址进行的,所以要进行地址转换。

如果多个作业都是为某一个程序服务,当主程序调用这些作业的时候,就需要定位到这些作业,保证程序的运行(主函数和分别编译好的子函数)。这时,链接的时候,链接器按照我们的分区机制来定位程序。

定位的常见方法:

  静态重定位

  当用户程序被装入内存时,一次性实现逻辑地址到物理地址的转换,以后不再转换(一般在装入内存时由软件完成)。

  动态重定位

  在程序运行过程中要访问数据时再进行地址变换(即在逐条指令执行时完成地址映射。一般为了提高效率,此工作由硬件地址映射机制来完成。硬件支持,软硬件结合完成)。硬件上需要一对寄存器的支持。

存储保护

保护系统程序区不被用户侵犯(有意或无意的)
不允许用户程序读写不属于自己地址空间的数据(系统区地址空间,其他用户程序的地址空间)

保护过程——防止地址越界

每个进程都有自己独立的进程空间,如果一个进程在运行时所产生的地址在其地址空间之外,则发生地址越界。即当程序要访问某个内存单元时,由硬件检查是否允许,如果允许则执行,否则产生地址越界中断,由操作系统进行相应处理。

一般由硬件提供一对寄存器:
基址寄存器:存放起始地址
限长寄存器:存放长度
(上界寄存器/下界寄存器)

保护过程----防止操作越权

对于允许多个进程共享的存储区域,每个进程都有自己的访问权限。如果一个进程对共享区域的访问违反了权限规定,则发生操作越权。即读写保护。

 

4.2 交换

交换技术的基本原理如图所示。开始时只有进程A在内存,随后进程B和C被创建,或者是从磁盘上换入。在图中,A被交换到磁盘上,然后D进入,接着B离开,最后A被重新调入。由于进程A现在是在不同的位置,它所包含的地址都必须重定位,或者是在它换入时由软件来完成,或者是在程序运行时由硬件来完成。

上图中的内存分配与固定分区的区别是:在固定分区中,分区的个数、位置和大小都是固定不变的;而在可变分区中,这些参数都是随着进程的进进出出而动态变化的。

随着进程的换入换出,在内存中会造成一些不连续的黑洞(即比较小的空闲分区),这时可以采用内存紧缩技术,也就是说,把所有的进程都尽可能地往内存的低端移动,相应的,那些空闲的小分区就会往地址的高端移动,从而在地址高端形成一个较大的空闲分区。

新的问题:

如果根据进程需要给它分配内存,而且,进程运行的时候大小不再变化,那么分配就简单,根据需要的大小分配即可;
如果进程运行时,该进程的数据段增长,就需要分配新的内存,那进程所需的空间就开始变大,上图中,如果相邻区域是空洞,则直接分配给它,但如果是进程时?交换,将某一进程移出,但此时内存满了,磁盘交换分区也满了,某进程则必须等待或杀死。

新问题的解决带来的矛盾:

多数的进程都需要动态的空间,且申请新的内存空间,频繁的交换导致大量的进程等待和时间开销。

矛盾的解决:

在每个进程申请空间的时候分配一些多余的内存空间;(A)
或者对于具有两个可增长数据部分的进程,采用在进程的分区内分开放置的方式管理。(B)

 

4.2.1 基于位图的存储管理

由于操作系统需要对内存进行分配管理和回收,所以,它必须很好地对内存进行跟踪,常用的是位图法和自由链表:

对于位图方法,位图的大小与内存的大小和分配的单位的大小相关,内存不变时,被分配的单个单元越小,则位图就越大。
位图的组织结构简单,便于管理,且占用的内存很小但也有缺陷:定位的速度受限,由于每个位图单元代表一个被分配的单元,所以连续性不强,对于某个需要几个分配单元大小的进程就很难快速在位图中找到这样连续的空间。故实际不很常用。

 

4.2.2 基于链表的存储管理

这种方法是通过一个链表来管理已经分配的和尚未分配的内存段,通过对链表的维护达到对内存的管理。这里的内存段可以使被某个进程所占用,也可以是个空洞,即尚未分配的区域。
表的内容包括段的性质、开始地址、长度、长度和指向下一个表项的指针。这是一般,链表的组织都按照地址来排列,这样的进程切换会很直观,更有效的方法是采用双向链表来记录。
但这时就需要对空洞进行处理。

对于某进程X结束的时候同邻居合并的四种方式

 

对于这种内存的组织和管理方法,我们需要讨论如何用合理的方法来为新创建的进程和新换进来的进程分配大小:

最简单的分配算法—最先匹配算法(first fit)
存储管理器沿内存段链表从头开始寻找足够的空间来装载进程,除非找到的这个区域的大小大于或等于进程的大小,把这个空间拆为两部分,一部分来装进程,另一部分是剩余的空闲区域。与之相对应,链表结点也要一分为二,分裂成两个结点,并修改相应的内容,包括分区的状态、起始地址和长度。
属于快速算法,搜索的开销很小。

最先匹配的变形—下次匹配法(next fit)
工作方式与首次适配相同,区别在于每次搜索完毕后记录当前的位置,下次搜索时从此处开始搜索。(缺点:较大的空闲分区不易保留)

最佳匹配法(best fit)
每次搜索整个链表中最适合该进程大小的内存空间,没有完全相同就寻找次之的;每次都要搜索整个链表故速度很慢,而且每次都可能拆分内存,而且生成的新的内存空间会很小。

放弃最佳匹配算法—采用最坏匹配法
采用最差,即每次找最大的内存空间分给进程,这样就不会产生大量的极小的内存空洞,即被分开后剩余的那部分仍然足够大,可以继续被使用。


对于这四种算法,都不是很有效的方法,如果把表示进程占用的内存段和表示空闲的内存段分开放到两个链表中,这样检索效率会提高很多,但这样带来的新麻烦就是控制过程的复杂化和内存释放过程的延长,不得不把释放的内存从一个链表中清除再放到另一个中去。

一些改进思路:

进程和空洞(空闲区)存放在两个链表中,针对最佳匹配算法的缺陷,将空洞的组织不按地址,而是按照大小来组织,这样就减小了搜索时的开销。
进程和空洞存放在两个链表中,用空洞本身来存放这个链表,第一个字存放空洞的大小,第二个字存放下一个空洞的地址。
快速匹配法,按照常见进程需要的空间大小设置单独的链表,这种算法就根据进程所需空间的大小快速在这些特殊的空洞链表中查找空内存。它需要将所有的空洞进行排序,即进程换出时的空洞合并判断。

 

posted @ 2017-06-02 22:35  王勋广  阅读(543)  评论(0编辑  收藏  举报