存储模型
1 地址重定位
地址重定位是在存储模型中非常重要的概念,也叫做地址转换、地址映射、地址翻译。在讲解地址重定位之前,需要知道以下知识:
- 程序装载到内存才可以运行,通常程序以可执行文件格式保存在磁盘上
- 多道程序设计模型中允许多个程序同时进入内存,在内存中不同的进程都会占据一部分空间
- 每个进程有自己的地址空间,一个进程执行时不能访问另一个进程的地址空间,进程不能对内存执行不适合的操作
在存储模型中需要解决的问题?进程有自己的地址空间,进程要执行必须要装载进内存才能执行,对于多道程序设计模型,多个进程都要装载进物理内存中,每个进程都需要占据物理内存中的一部分空间,因此在存储模型中需要解决如何把进程地址空间的内容装载进物理内存中,使每个进程正确运行。
进程地址空间
进程地址空间通常一分为二:内核地址空间和用户地址空间,其中用户地址空间包含进程的代码和数据,以及在运行过程中可能扩展的堆和栈,还有一部分空间用来存放共享库、内存映射文件等其他内容。如下图所示是linux进程地址空间的布局
通过以上分析可以知道:进程中的地址不是最终的物理地址,在进程运行前无法计算出物理地址,因此需要一种机制实现进程地址空间向物理地址的转换,从而将进程加载进内存执行,即需要地址重定位机制的支持。
地址重定位
这个概念涉及到逻辑地址到物理地址的转换
- 逻辑地址(相对地址、虚拟地址):用户程序经过编译、汇编后形成目标代码,目标代码通常采用相对地址的形式,其首地址为0,其余地址都相对于首地址而编址,不能用逻辑地址在内存中读取信息
- 物理地址(绝对地址,实地址):内存中存储单元的地址,可直接寻址,处理器可以直接从物理内存中取得指令和数据
为了保证CPU执行指令时可正确访问内存单元,需要将用户程序中的逻辑地址转换为运行时可由机器直接寻址的物理地址,这一过程称为地址重定位。
地址重定位的方式
- 静态重定位: 当用户程序加载到内存时,一次性实现逻辑地址到物理地址的转换,一般可以由软件完成。优点是程序的执行过程中可以直接使用物理地址寻址,但是程序在内存中的位置不会改变,一旦改变就需要重新计算物理地址才能运行。
- 动态重定位:程序在装载进内存之后不会立即将逻辑地址转换为物理地址,而是在进程执行过程中进行地址变换,即逐条指令执行时完成地址转换,需要硬件部件支持。这是经常使用的重定位方式,因为程序在执行过程中位置要进程改变,一个进程可能被多次换出内存,又被多次换入,而每次换入后的位置通常是不同的。
如下图所示,当一个进程加载进内存之后就知道该进程在物理内存的首地址,假设为12000,将物理内存的首地址写入重定位寄存器,CPU取到进程的逻辑地址假设为456,经过内存管理单元(MMU)完成地址转换的工作,得到进程在内存的物理地址,CPU根据物理地址取得指令和数据。
2 物理内存管理
空闲物理内存管理
对物理内存可以有不同的划分,可以是等长划分,即把一片空闲的物理内存划分为等长的区域,每个区域称为一个分配单元,某个进程进内存的时候可能分配若干了分配单元满足进程对内存的需求;第二种划分是不等长划分。需要不同的数据结构来管理物理内存。
等长划分可以使用位图这种数据结构:每个分配单元对应于位图中的一位,0表示空闲,1表示占用(或者相反)。
不等长划分可以使用空闲区表、已分配区表这种数据结构:表中每一项记录了空闲区(或已分配区)的起始地址、长度、标志,也可以使用空闲块链表来表示。
内存分配算法及回收
- 首次适配 first fit:在空闲区表中找到第一个满足进程要求的空闲区
- 下次适配 next fit:从上次找到的空闲区处接着查找
- 最佳适配 best fit:查找整个空闲区表,找到能够满足进程要求的最小空闲区
- 最差适配 worst fit:总是分配满足进程要求的最大空闲区
内存分配算法将该空闲区分为两部分,一部分供进程使用,另一部分形成新的空闲区;内存回收算
当某一块归还后,前后空闲空间合并,修改内存空闲区表。
伙伴系统
Linux低层内存管理采用的内存分配方案,主要思想是将内存按2的幂进行划分,组成若干空闲块链表;查找该链表找到能满足进程需求的最佳匹配块。 算法设计如下:
(1)首先将整个可用空间看作一块: 2^U
(2)假设进程申请的空间大小为s,如果满足2^U-1<s<=2^U,则分配整个块;
否则,将块划分为两个大小相等的伙伴,大小为2^U-1
(3)一直划分下去直到产生大于或等于 s 的最小块
3 基本的内存管理方案
首先介绍单一连续区、固定分区、可变分区这三种内存管理方案,特点是整个进程进入内存中一块连续的区域。
(1)单一连续区
在单道程序环境下,当时的内存管理方式是把内存分为系统区和用户区两部分,在用户区内存中,仅装有一道用户程序,即整个内存的用户空间被改程序独占。特点是一段时间内只有一个进程在内存,简单但是内存利用率低
(2)固定分区
把内存空间分割成若干区域,称为分区,其中每个分区的大小可以相同也可以不同,每个分区一旦确定分区的大小就不会改变,每个分区装一个且只能装一个进程。
(3)可变分区
根据进程的需要,把内存空闲空间分割出一个分区,分配给该进程,剩余部分成为新的空闲区。但是这种分区方式会产生较多的外碎片(指两个进程占据内存空间之间少量的空闲内存空间),导致内存利用率下降。
碎片是指很小的、不易利用的空闲区,会导致内存利用率下降。可以利用紧缩技术(memory compaction)来解决碎片问题,在内存移动程序,将所有小的空闲区合并为较大的空闲区,又称为压缩技术,紧致技术,搬家技术。在解决碎片的时候需要注意对内存进行压缩可能会造成时间和空间的开销,另一方面不是所有的进程都可以执行紧缩技术,例如一个进程正在执行I/O操作时就不能移动进程,否则会造成I/O结果错误。
接下来介绍另外三种内存管理方案:段式、页式和段页式,特点是一个进程进入内存中若干个不连续的区域。
(4)页式存储方案
设计思想:用户进程地址空间被划分为大小相等的部分,称为页(page)或页面,从0开始编号;内存空间按同样大小划分为大小相等的区域,称为页框(page frame),从0开始编号,也称为物理页面,页帧,内存块。以页为单位进行分配,并按进程需要的页数来分配;逻辑上相邻的页,物理上不一定相邻。
逻辑地址在页式存储方案中由两部分组成:页号和页内地址(位偏移),这个划分是由系统自动完成的。