@我是博客初哥,如若写得不好,请多多提点。希望大家多多补充,互相学习。


        前言:这里的存储器一般是指内存,内存不仅为CPU提供指令和数据,还有和I/O设备数据交换,可见存储器的位置多么关键,所以为了改善CPU的资源利用率和对I/O的吞吐,如何管理存储器变得很重要。存储器管理是操作系统的功能之一,它包括存储器的分配、保护和共享,后来引入了虚拟内存技术来解决物理内存小于进程地址空间的问题。


        进程申请的逻辑空间就叫做地址空间,物理内存的空间就叫做存储空间。    


        在单道批处理系统和单用户单任务系统中,内存的分配方式是单一连续区分配,也就是说一个作业(用户程序)占用其用户进程区,所以,这一个作业就占用了所有的系统资源,可想而知,资源的利用率很低。内存是有分区的,有用户进程区、RAM区(操作系统驻留)、ROM区(BIOS,存放设备驱动)。如果用户程序能够跨越内存的区域操作,就会影响到系统,容易受到病毒攻击,就如DOS系统,它是没有存储保护功能的。单一连续区分配的存储保护,只需要限定CPU访问内存地址的范围。如图下,要限定m<addr<n。


存储器管理_存储器


         对于多用户多道程序系统,它把内存分为若干个连续的区域,内存的分配方式是分区分配,分区分配又分为固定式分区、可变式分区。分区分配实现了存储器的共享功能,所谓共享,也就是说相同的程序或数据在内存上保留一份即刻,其代码不会在执行过程中被修改。

        固定式分区就是预先分好不同大小的的区域,当进程申请空间的时候,它会去找最符合而且最小的区域;当遇到被占用,就会等待。这种管理方法简单,但是很可能会造成大区域的利用率少,小区域的队列拥挤。因为不可能每个进程的大小都跟预设好的区域一样,所以这样方法对资源的利用率比较低。它使用的地址就是主存的绝对地址,也就是物理的内存地址。

        可变式分区比起固定式分区灵活得多,它是根据进程要求申请的空间大小来划分内存区域的,这样分区方法由分区说明表和空表区链来记录相关的信息。当系统回收一个释放区的时候,它会和隔壁的空闲区合并成一个。当分配内存的时候,根据不同的需要用不同的分配算法:

(1)首次适应(first fit),从起始地址最小的空闲区开始扫描,直到找到一个足够大的空闲区为止;

(2)最佳适应(best fit),找到满足进程需求的最小空闲区为止;

(3)最坏适应(worst fit),找到满足进程要求的最大空闲区为止;这种方法的缺点是:当一个更大的进程申请的时候,就没有空间给它申请了。

        以上的所有方法都是基于物理存储空间>=进程地址空间,但是这个是不现实的。覆盖技术和交换技术就是在逻辑上扩充了内存。

        程序是算法和数据结构组成,算法当中,有很多的语句,譬如循环语句、分支语句等等,在执行循环语句时,它会在当中一直反复地执行某一部分的语句,这就是时间局部性;当执行分支语句的时候,如果分支1和分支2是互斥的,也就是说我选了分支1,分支2就不用被执行了,也就是我们可以先不把它放到内存上,等需要的时候再把分支1拿下,用分支2代替,这就是空间局部性。时间局部性和空间局部性就是程序的局部性原理

        覆盖技术就是通过程序的空间局部性,打破了所有进程地址空间装入存储空间后才能执行。这个技术的关键是提供正确的覆盖结构。

        交换技术就是当内存空间,进程用完分配的时间片或者等待I/O时,把内存中暂时不用的信息先转移到硬盘上,在硬盘上的那部分文件就叫做交换文件。

        说了那么多存储器管理方法,是不是头都晕了?挺住,下面还有。在我们不断地申请和释放内存空间的时候,会产生碎片。碎片分为外部碎片和内部碎片,内部碎片就是你被分配了空间,但是有一部分你是没有用到的;外部碎片就是空间很小,小得不能再利用来分配。其实我们可以通过拼接结束来把碎片拼接起来重新使用,但是这样很耗内存的稳定性。为了克服碎片问题,页式管理就诞生啦。页式管理把主存分割成相同大小(2的n次方)的若干块,称为页框,进程的地址空间也划分成相同大小的若干块,称为虚页,虚页包括10个字节的页号和10个字节的页内地址(逻辑地址)。页表是逻辑地址和内存块的桥梁,映射了他们两个的关系,页表是存放在内存上的,而且它在内存的始址上。当我们运行程序的时候,程序看到的是进程页,看到自己哪个地址对应着哪条语句,看到的那个地址对应的页号跟页表长度比较,如果页号>=页表长度,就是它访问的地址越界了(还记得内存用户操作区吗?这里的话是超出了分配给程序的内存空间),就终止运行了。如果页号<页表,由(页表始址+页号X页表项占用的字节数)得到了该页号在页表的入口地址,然后就取得了对应的内存块号。这就是页式管理的地址变换。于是我们就可以访问到对应的语句。再说说页式管理的保护,其实就是linux文件的rwx标志位的机制。

        但是每次访问操作都产生了两次对内存的访问:一次是查询页表,一次是访问对应的块号。为了克服这个缺点,我们引入了TLB(联想存储器translation lookaside buffer)和快表。其实就是把经常访问的写在快表,在查询的时候快表和页表一起查询,当快表查到的时候,页表查询就中断。

        说完页式管理,还有一个就是段式管理。我们的程序是若干个程序段或数据段组成的,这样更符合程序运行的逻辑。为此,段式管理每个进程的地址空间都是按照程序自身的要求而划分成若干段的。所以它的分配是随机的,所以会产生碎片。其余的话跟页式管理差不多。

        最后一个啦!之前所说的页式管理和段式管理,都是在进程的空间能够完全映射在物理内存空间上,这是不现实的,所以又推出了比较成熟的页式虚拟存储管理、段式虚拟存储管理。其实就是页式管理+交换技术,段式管理+交换技术!够简单吧?不过我要补充的是,当内存满了的时候,我们要考虑把内存上某一页置换出来,而根据什么来置换,就要根据页面淘汰算法了。其实页面淘汰算法才是重点,有FIFO、NUR、OPT等等。有兴趣的可以去看一下(我这里有对应的程序)。

        最后要补充的是,页面保护的问题,当一个页被共享给多个进程读的话,这个是完全没问题的,但是如果多个进程在写的话,就会有问题了:1.如过某个进程在写的时候,我把页置换掉了,那原本的资料是不是完蛋了?2.你改别人的资料别人知道吗?你家人知道吗?你老板知道吗?所以,这里有一个保护机制就做写时复制技术,就是你要写的时候,就复制一分给你自己用,你在上面任意写都可以。别人的那份还是没有变。


        总结,单用户单任务系统中,一个作业占用全部用户操作区,资源利用率低,其存储保护是通过限制CPU访问内存地址的范围;多用户多道程序系统中,采用分区分配:固定式分区,可变式分区,后者比前者分配的时候更灵活,利用程序的局部性原理,我们可以通过覆盖、交换技术在逻辑上对内存的扩充。页式管理克服了碎片,支持共享和保护,联想存储器(TLB)和块表克服了两次访问主存的缺点;段式管理类似与页式,但是页式大小是固定不变的,而段式是又程序地址空间决定的。虚拟内存采用了不同的页面淘汰算法来置换页面。


        写后感:这算是第一次认真地把一片技术博客写完,怎么说呢,其实我写这篇博客花了一天多,一部分原因是因为自己的逻辑思维没有通,另一部分是写着写着就累了,有些地方写得不是很好,希望大家见谅。不过我还是会继续写博客的,万事开头难,我相信我会越写越顺利的。加勒个油的,摇滚荣。


@教材:《操作系统原理教程(第三版)》-张丽芬 刘美华编著