内存的分配与回收
早期的计算机编程并不需要过多的存储管理,但随着计算机和程序越来越复杂,存储管理成为必要。存储管理需要确保计算机有足够的内存处理数据、确保程序可以从内存中获取一部分内存使用,并可以归还使用后的内存以供其他程序使用。
内存分配
- 单一连续分配:
单一连续分配是最简单的内存分配方式,只能在单用户、单进程的操作系统使用。它把系统内存分为系统区和用户区,系统区内的内存只能够被操作系统使用,用户区内的内存只能被用户程序使用。这个分配方式已经过时不再使用了。
- 固定分区分配:
固定分区分配是支持多道程序的最简单存储分配方式。内存被划分为若干固定大小的区域,每个分区只提供给一个程序使用,互不干扰。
- 动态分区分配:
这是操作系统中比较常用的方法。根据进程实际需要,动态地分配内存空间。要支持这种分配方法,就需要相关的数据结构和分配算法:
数据结构:
假设主存中有若干分区,有些分区已经被使用,有些分区空闲。动态分区空闲表这个数据结构就可以记录分区的使用情况。
还有动态分区空闲链数据结构,用一个双向链表的结构存储来存储当前系统中的空闲区域。
算法:
首次适应算法(FF):主要使用空闲链的数据结构,每次进行内存分配的时候从链表头开始顺序查找适合的内存区域。遍历结束后发现没有合适的空闲区,则该次分配失败;如果找到了合适的空闲区,则把这块内存分配给进程使用。首次使用算法有个很大的问题:由于每次都是从头部开始遍历,使得头部的地址空间不断的被划分,导致头部地址可能出现大量碎片。对于这个问题,可以优化为循环适应算法,每次遍历结束后记录位置,下次遍历从这个位置开始,而不需要每次从头开始遍历。
最佳适应算法:空闲链表按照容量大小进行排序,每次遍历空闲区链表时可以找到最佳的合适空闲区。这种算法可以有效避免大材小用的情况。
快速适应算法:快速适应算法要求有多个空闲区链表,每个空闲区链表存储一种容量大小的空闲区。这样当我们需要某个大小的空闲区域时,就可以快速找到对应的链表取内存。
内存回收
内存回收的场景可能会遇到下面四种场景:
第一种场景是需要回收的区域和空闲区连在一起且正好在空闲区后面,这个场景非常简单,不需要新建空闲链表节点,只需要把空闲区的容量增大即可,把回收区的容量包括进去。
第二种情况是需要回收的区域和空闲区连在一起,但在空闲区前面。这个场景也是将回收区和空闲区合并,合并而成的新的空闲区使用回收区的地址。
第三种情况回收区恰好在两个空闲区之间,回收需要将空闲区1、空闲区2和回收区一起合并,然后新的空闲区地址使用空闲区1的地址。
最后的场景就是单一的回收区,没有连接任何的空闲区,这种情况要为回收区创建新的空闲节点,然后把回收区插入到相应的空闲区链表中去。
段页式存储管理
每个进程都有独立的进程空间,操作系统管理进程空间有三种管理方式:
页式存储管理
计算机组成原理中有字和字块的概念,字和字块是相对内存条等物理设备的定义,而操作系统内存管理中的页面则是相对逻辑空间的定义。字块和页面都是指大小一样的一块内存。
页式管理是将进程的逻辑空间划分成若干大小相同的页面,把相应的物理内存空间分成与页面大小相同的物理块。以进程为单位把进程空间装进物理内存中分散的物理块中。
页式管理系统中需要了解内存碎片的概念。如下图所示,空闲链表中存有大小不同的空闲节点,如果需要申请一个页面大小的内存,节点1不够,只能使用节点2-3的一部分,这样就造成了内存碎片。所以页式存储管理的页面大小应该适中,过大的话难以分配,过小的话又会造成内存碎片多。一般情况下,页面大小为512B~8K。
为了知道某个页面被分配到哪个字块,还需要了解页表的概念。页表记录进程逻辑空间和物理空间的映射关系。
在现在计算机系统中,可以支持非常大的逻辑地址空间(
单纯的页式存储管理可能遇到的问题是,假如有一段连续的逻辑分布在多个页面中,将大大降低执行的效率。
段式存储管理
段式存储管理将进程的逻辑空间划分成若干段,这种划分是非等分的。每一段的长度由进程内连续的逻辑长度决定,比如针对MAIN函数、子程序段X、子函数Y等,根据不同的长度分配不同的内存空间。
我们也需要一个数据结构来保存逻辑空间到物理空间的映射,段式存储结构中的数据结构叫段表,由于段长度不同,所以相比页式存储管理,段表内多了一个段长。
页式存储管理和段式存储管理相同点在于,都离散的管理了进程的逻辑空间,它们之间的不同点在于:
- 页是物理单位,段是逻辑单位;
- 分页是为了合理利用空间、分段是为了满足用户需求;
- 页的大小由硬件固定,段的长度可动态变化
- 页表信息是一维的,段表信息是二维的(需要记录段长度)
段页式存储管理
结合了页式存储管理和段式存储管理,产生了段页式的存储管理。分页的优点在于虽然存在页内碎片,但相比分段来说提高了内存的利用率;分段的优点在于可以更好的满足用户需求。
段页式存储管理首先将逻辑空间按照段式管理分成若干段,再把段内空间按页式管理分成若干页。页式管理的页地址由页号和业内偏移组成,段式管理的段地址由段号和段内偏移组成,段页式结合两者,段页地址由段号、段内页号、页内地址三者组成。
虚拟内存
有些进程实际需要的内存很大,超过了物理内存的容量,而伴随着多道程序设计的出现,使得每个进程可用的物理内存更加稀缺。但由于现实的原因,不可能无限增加物理内存,物理内存总有不够用的时候,所以诞生了虚拟内存技术。虚拟内存是操作系统内存管理的关键技术,使得多道程序运行和大程序运行成为现实。虚拟内存技术把程序使用的内存进行划分,将暂时不使用的内存放置在辅存中。
程序的局部性原理
局部性原理是指CPU访问存储器时,无论是存取指令还是存取数据,所访问的存储单元都趋于聚集在一个较小的连续区域中。
局部性原理是虚拟内存可以实现的原因之一。程序加载的时候,不需要把进程所有的逻辑空间都加载到内存中,只装载部分即可。如果需要访问页时发现页面不在内存中,则发出缺页中断,发起页面置换,置换后程序就可以继续运行下去。从用户层面看,程序仿佛拥有很大的空间,即是虚拟内存。虚拟内存实际上是对物理内存的补充,速度接近于内存,成本接近于辅存。
虚拟内存置换算法
- 先进先出算法(FIFO)
- 最不经常使用算法(LFU)
- 最近最少使用算法(LRU)