操作系统采用哪些方法提高内存利用率
从操作系统的需求开始说起
要想搞清楚操作系统采用哪些方法来提高内存利用率,首先,我们应该明白操作系统为什么需要提高内存的利用率。
计算机的系统资源分为处理机、存储器、I/O设备以及文件(数据和程序),相应地,操作系统的主要功能也是对这四类资源的有效管理。
而存储器作为计算机系统的重要组成部分,虽然容量一直在随着计算机技术的发展而不断扩大,但仍不能够满足现代软件发展的需要。它仍然是一种宝贵而又稀缺的资源。因此,如何对存储器进行有效的管理对系统的性能有重大的影响。
操作系统进行存储器管理的主要任务,是为多道程序的运行提供良好的运行环境,提高存储器的利用率,方便用户使用,并能从逻辑上扩充内存。而用户程序需要在系统中运行,必须先将它装入内存,然后再经过编译、链接、装入将其转变为一个可以执行的程序。
至此,可以说操作系统对存储器的管理至关重要,其中最需要关注的问题是对内存的分配和回收。
速度和容量,我们永恒的目标
在计算机执行时,几乎每一条指令都涉及对存储器的访问,因此要求对存储器的访问速度跟得上处理机的运行速度。或者说,存储器的速度必须非常快,能与处理机的速度相匹配,否则会明显地影响到处理机的运行。此外还要求存储器具有非常大的容量,而且存储器的价格还应该很便宜,可靠性好等。单个存储器件无法同时满足,所以现代计算机中采用了多层结构的存储器系统。
存储器的分配、回收以及对存储层次间数据移动的管理都会影响到存储器的利用效率。在操作系统的层面,我们主要考虑如何提高对存储器的访问速度,并从逻辑上扩充其容量。
因此我们对存储器的目标是,提高其存取速度,使之与CPU的取指速度相匹配;逻辑扩充其容量,使之能装下当前运行的程序和数据。
将程序装入内存时遇到的问题
早期的操作系统的做法是为一个用户程序分配一个连续的内存空间,即程序中代码或数据的逻辑地址相邻,体现在内存空间分配时物理地址的相邻。为了适用于多道程序的运行环境,程序的装入和链接需要在真正执行时执行。
最初的固定分区分配,是将整个用户空间划分为若干个固定大小的区域,在每个分区中之只装入一道作业。这样必然会造成存储空间的很大浪费。我们更希望根据进程的实际需要动态分配内存空间。需要分配内存时,在空闲分区中按请求的大小划分出一块内存空间;在内存回收时再将分区释放,并插回空闲分区。
为了能够提高内存利用率,进一步提升系统性能,现行有很多动态分区分配的算法。主要分为基于顺序搜索和基于索引搜索两种。
基于顺序搜索的动态分区分配算法,是指一次搜索空闲分区链上的空闲分区。可以按照顺序搜索时的分区策略分为首次适应算法(优先利用低址部分的空闲分区)、循环首次适应算法(从上次找到的空闲分区开始查找的首次适应算法,均匀利用空闲分区)、最佳适应算法(优先利用满足要求的最小的空闲分区)和最坏适应算法(优先利用最大的空闲分区)。
但是,不管采用什么样的优先级,剩余的不连续小空间都无法被很好的利用。并且,对大内存的系统来说,顺序搜索每次的查找操作的开销较大,需要进一步搜索空闲分区的速度。由此,我们考虑采用基于索引搜索的动态分区分配算法。
如果考虑将相同容量的空闲分区放在同一类,并设立管理索引表进行管理,每次只需根据进程的长度查找索引,便可以很大程度上减少查找的开销。这就是快速适应算法。但是,由于设立额外的索引表需要占用空间,所以快速适应算法的时间性能虽然优于顺序搜索算法,但是它的空间性能与顺序搜索相比较差。
进一步进行算法优化,在伙伴系统算法中,我们规定分配分区和空闲分区的大小都为2k(1≤k≤m,内存空间的大小为2m)。需要分配2i大小的空间时,优先找到大于当前要求的最小空闲分区2i+1,如果没有,则找大小为2i+2的空闲分区,以此类推,在找到后对其进行分割。需要回收2i大小的空间时,将其与大小相同的2i的空闲分区进行合并,并将合并后的2i+1的空闲分区与和其大小相同的空闲分区进行合并,以此类推。由于需要对分区进行分隔和合并操作,其时间性能优于顺序搜索,但差于快速适应算法。在空间上,由于提高了空闲分区的可使用率,其空间性能优于快速适应算法,但差于顺序搜索算法。
但是使用分类的方法仍会需要较大的空间开销,直到哈希算法的出现才解决了这一问题。以空闲分区的大小为关键字建立哈希函数,构造一张能够记录空闲分区链表表头的哈希表。这样一来,既可以优化时间性能,又可以避免设立索引表的空间开销。能够较好地提高内存的利用率。
但是无论是顺序搜索还是索引搜索,以上的算法仍然遇到了一个共同的问题——“碎片”问题。内存空间会被分割为许多难以利用的、很小的空闲分区。由于小分区并不连续,即便其容量总和大于要装入的程序仍无法利用。这样的小分区被称为“碎片”。
如何解决碎片问题对内存分配和回收至关重要。一种解决想法是,在产生的“碎片”之后再次对其进行重新利用。将内存中的所有作业进行移动,使之相邻接,把原来分赛的多个空闲小分区拼接成大的分区,这就是动态可重定位分区分配算法。要实现对地址的修改,必须有硬件的支持,即设置重定位寄存器,并且算法必须具有使已使用的内存空间“紧凑”的功能。这样便会大大地影响到系统的效率。同时,考虑到用户程序共享程序和数据的需要,动态重定位的算法仍会造成许多内存空间的浪费。
为了能够进一步提高内存的利用率,彻底解决“碎片”问题,考虑打破为进程分配连续内存空间的连续分配方式,而将进程分散放入各个不相邻的分区中。这就是离散化的分配方式。
离散化的分配方式
离散化分配空间,是指将用户程序的地址空间分成若干个区域,分别进行内存的分配和回收。在划分区域时,不同的划分方式。为了提高内存利用率,我们首先想到的是分页存储管理。
分页存储管理,是将用户程序的逻辑地址空间分为若干个固定大小的区域,这些区域称为 “页面”, 简称为 “页”,相应地,将内存物理地址空间也分为若干个固定大小的 “物理块”,简称为 “块”。在为进程分配内存时,将进程的若干页以块为单位装入内存中。
为了能够在内存中找到每个页面所对应的物理块,需要为进程创建一张页面映像表,简称为页表。页表的表项记录相应页在内存中对应的物理块号。
程序逻辑地址由页号和页内地址组成,内存中的物理地址由块号和块内地址组成。这样就可以通过查找页表完成从逻辑地址到内存中物理地址的转换。
在进程运行期间,每次存取一个数据都需要进行地址变换,这都会进行两次的内存访问操作(一次访问页表,一次访问物理地址)。反复进行地址变换造成的时间开销很大。为了提高地址变换的速度,可在地址变换机构中增加一个“联想寄存器”,也叫做 “快表”。对快表进行存取的速度快于内存,在快表中存放较常访问到的页表表项,并在查找地址时优先访问快表,可以极大地缩短地址变换的时间开销。
现代计算机系统的逻辑地址空间多为32位或64位,其页表所占空间也非常大,需要占用相当大的内存空间。这时我们考虑再次对页表进行离散化的分配方式。
为页表再次进行分页,并建立 “外层页表”进行存储。外层页表的表项记录页表分页的首地址。可以通过外层页表和页表共同实现从逻辑地址到内存中物理地址的转换。这时程序逻辑地址由外层也好,外层页内地址和页内地址组成。
相似地,可以将两级分页拓展到多级的离散化分页。目前在64位计算机中,多数使用三级页表结构。
随着操作系统的不断发展,为了能够满足程序员用户在使用上的要求,并且反映了程序本身的逻辑结构,又引入了分段存储管理的离散化分配方式。虽然其目的不是直接提高内存的利用率,但由于分段存储管理便于实现、分段可共享、易于保护、可动态连接等优点,采用分段存储管理方式也有利于高效管理内存空间。
分段存储管理,是将用户程序的逻辑地址空间分为若干个大小不同的区域,这些区域称为 “段”。每一个段是一个相对独立的逻辑单位。需要创建段映射表,简称为段表。段表的表项记录相应段在内存中对应的物理块号。分段存储的程序逻辑地址由段号和段内地址组成。也可以设置一个“联想寄存器”缩短对分段式存储的地址变换的时间开销。由于段比页大,段表项的数目比页表项多,所需要的联想寄存器较小,可以显著减少存取数据的时间。
分页系统以页面为内存分配的基本单位,能有效地提高内存利用率;分段系统作为内存分配的基本单位,能够更好地满足用户多方面的需要。如果能够将两种存储方式结合,则可形成一种新的存储器管理方式——段页式存储管理方式。
段页式系统的基本原理是分段和分页原理的结合,即先将用户程序分成若干个段,再把每个段分成若干个页。为了实现从逻辑地址到物理地址的变换,系统需要同时配置段表和页表。段表的内容是段号,页表大小和页表始址,同时,为段表和页表都创建了一个状态位。段页式系统的逻辑地址由段号,段内页号和页内地址组成。
由于在访问内存时需要经过访问段表,访问页表,访问物理地址共三次内存访问操作,将访问内存的次数大大增加。为了提高执行速度,需要在地址变换机构中需要增设一个快速联想寄存器。每次访问它时,同时利用段号和页号检索寄存器,其基本原理与分页及分段的情况相似。
由覆盖到对换的虚拟存储
存储器性能的提升分为存取速度和存储容量两个方面。随操作系统的发展,虽然存取速度已经基本能与CPU持平,但处理空间很大的作业时,其存储容量仍然有限。存储管理从连续分配方式发展到离散分配方式,是通过改变作业大小提供解决方案。更直观的想法是增加内存的容量。
在物理上增加内存容量往往会增加系统的成本,并且很难达到理想的容量大小。为了解决内存空间不足的问题,进一步提高内存利用率,早期的操作系统采用了基于覆盖技术的存储管理。
覆盖技术的基本思想是,对同一个进程进行逻辑上互斥划分,分为各个覆盖。相互独立的覆盖可以不同时被调入内存运行。
虽然能够将较大的作业装入内存运行,但是,由于覆盖程序段的最大程度仍然受到内存容量大小的限制,覆盖技术仍然有较大的局限。
如果考虑把暂时不用的程序进程全部由内存移到外存中,在需要时再将其调入内存,便可以更大程度上解决内存空间不足的问题。这就是对换技术。对换技术的出现打破了一个程序一旦进入内存便必须一直运行直到结束的限制,能够提高内存的利用率。但是,由于对换技术换入换出内存的基本单位是整个进程,一个进程的大小仍然收到内存容量大小的限制。
而虚拟存储器技术作为操作系统存储器管理的一项重要技术,它能够从逻辑上扩充内存容量解决这一问题。它允许一个作业部分装入内存就可以运行,并且支持多个用户程序并发执行。
在虚拟存储技术中,由于换入换出是以页或者段为基础单位的,程序的大小不再受到内存容量的限制,只受到CPU地质结构的限制。对请求分页的虚拟存储器技术来说,只要采用适当的调页策略,选择合适的页面置换算法,进行合理的内存分配策略,便能大大地提升系统的性能。请求分段的虚拟存储技术也与其类似。
总的来说,系统提高内训利用率的方法,主要有离散化内存分配方式和虚拟存储器技术两种。一种是将进程作业进行划分,减少对内存空间的浪费;一种是在逻辑上扩充容量,使得作业能够避免内存空间的限制。同时,设置高速的联想寄存器,能够有效节省访问内存的时间,提高存储器的存取速度。从操作系统的发展可以看出,内存利用率的提升离不开硬件的支持和合适的算法选择,为了提高内存的利用率,每一种硬件和算法的选择都需要开发人员的精心设计和细心实现。
在学习操作系统的设计原理的过程中,不仅仅要看到设计的原因,更要看到每一种设计的思维脉络,体会其中的思想。并且内化这样的思维方式,将其运用在代码开发中去。这才是我们学习操作系统的目的。