课本P198-P207
内存管理的主要操作是处理器把程序装入内存中执行。在几乎所有的现代多道程序设计系统中,内存管理涉及一种成为虚存(虚拟内存)的复杂方案。虚存基于分段和分页这两种基本技术,或基于这两种技术的其中一种。在虚拟内存应用之前,使用分区技术来进行内存管理。
1.什么是内存?
(1)内存是用于存放数据的硬件。程序执行前需要先放到内存中才能被CPU处理。
思考:在多道程序环境下,系统中会有多个程序并发执行,也就是说或有多个程序的数据需要同时放到内存中。那么,如何区分各个程序的数据是放在什么地方呢?
解决方案:给内存的存储单元编地址
(2)具体:
内存中也有一个个的“小房间”,每个小房间就是一个存储单元
内存地址从0开始,每个地址对应一个存储单元
2.几个常用的数量单位
有的题目会告诉我们内存的大小,让我们确定地址长度应该是多少?(即要多少个二进制位才能表示相应数目的存储单元)
例如:上面的例子
3.逻辑地址和物理地址
指令中的地址可以采用逻辑地址和物理地址的这种思想
已经被分配出去(能明确指出属于哪个进程)却不能被利用的内存空间
当一个进程装入到固定大小的分区块(比如页)时,假如进程所需空间小于分区块,则分区块的剩余的空间将无法被系统使用,称为内部碎片。(即被进程占用,没被进程利用)直到进程释放它,或进程结束时,系统才有可能利用这个存储块。
外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。
外部碎片是出于任何已分配区域或页面外部的空闲存储块。这些存储块的总和可以满足当前申请的长度要求,但是由于它们的地址不连续或其他原因,使得系统无法满足当前申请。
(1)向内存的一端移动进程,使碎片在另一端合并成一个大空闲区 费时,需动态重定位
(2)为了减少碎片:系统设一最小空闲区长度size,若即将产生的新空闲区小于size,则不再划分,而将整个空闲区分配。
伙伴系统适用于当内核中要求分配连续页时的快速检测内存中的连续区域
系统中的空闲内存总是两两分组,每组中的两个内存块称作伙伴。伙伴的分配可以是彼此独立的。但如果两个小伙伴都是空闲的,内核将其合并为一个更大的内存块,作为下一层次上某个内存块的伙伴。
(1)分配时,先查找是否有最合适尺寸s的空闲块,若有则分配 例如:需要60KB,则60KB<=s<120KB
(2)若没有则查看是否有长为2s的空闲块,仍没有则查找长4s、8s 、16s……的块,直至找到一个空闲块。(3)把该块对半分为两个伙伴L(被分配好的内存空间)和R(新的空闲区)
但若L过大,则继续重复对半分的操作,直到L的尺寸合适。所有的R都被保留作为新的空闲区(4) 回收时,L、R都空闲时,合并为一大空闲区,一直合并直到没有相邻的空闲区为止
(5)例子
PS:从上图可以看出左右伙伴合并的条件是左右伙伴的大小相等且空闲
设有一大小为2k(2的k次幂)、起始地址为x的块,buddyk(x)是其伙伴的地址
链接地址跟运行地址不同
(1)运行地址:程序运行的时候的地址,即用工具将代码下载到RAM的那个地址,也叫加载地址。
(2)链接地址:由链接脚本指定的地址。(相当于高级程序语言中(以c语言为例)的指针变量,在调用某函数时就会去这个地址编号去找该函数)
(3)为什么会出现链接地址跟运行地址不同的情况
因为当一块启动的时候,依靠内部的SRAM(静态随机存取存储器),可以运行一小段代码,此时DDR还未初始化,所以开始的运行地 址必然在内部的SRAM中。而在初始化之后所有的代码都是在DDR上运行的,所以链接地址必然是DDR上的地址。所以链接地址和运行地址不同。
补充:
(1)SRAM:是随机存取存储器的一种。所谓的“静态”,是指这种存储器只要保持通电,里面储存的数据就可以恒常保持。
(2)DDR:是一个内存名称,意思即双倍速率同步动态随机存储器,是内存的其中一种。
(3)内存:内存又可以叫做主存。是CPU能直接寻址的存储空间,由半导体器件制成。内存的特点是访问数据的速率快。
解决链接地址和运行地址不同有两种办法
(1)全部使用位置无关码。 (2)进行重定位让这两个地址相同。
但是如果是少量代码,使用(1)是可以的,但是一个大的代码文件很难保证全部都使用位置无关码的,这也是不现实的,所以必须使用重定位解决这个问题。
(1)逻辑(相对)地址:是指与当前数据在内存中的物理分配地址无关的访问地址。(相对地址是逻辑地址的一个特例,它是相对于某些已知点(通常是程序的开始处)的存储单位。)
(2)逻辑(绝对)地址:是指数据在内存中的实际位置
(1)程序被运行,地址的一个变化如下:
系统采用运行时动态加载的方式把使用相对地址的程序加载到内存。通常情况下,被加载进程中的所有内存访问都相对于程序的开始点。因此,在执行包括这类访问的指令时,需要依赖于硬件机制把相对地址转成物理内存地址
(2)硬件支持:
基址寄存器(处理器寄存器):程序在内存中的起始地址 界限寄存器:指明程序的终止位置
(3)在进程的执行过程中会遇到相对地址(包括指令寄存器的内容、跳转或调用指令中的指令地址以及加载和存储指令中的数据地址)
每个这样的相对地址都要经过以下两步转成物理地址:
①基址寄存器的值加上相对地址产生一个绝对地址
②该绝对地址与界限寄存器的值比较,若这个地址在界限范围内则继续执行该指令;否则向操作系统发出一个中断信号,操作系统必须以某种方式对这个错误做出响应
只要小于等于分区大小的任何进程都可以装入任何可用的分区中。若所有的分区都已满,且没有进程处于就绪态或运行态,则从操作系统可以换出一个进程的所有分区,并装入另一个进程,使得CPU有事可做。
固态分区的两个难点:
(1)程序可能太大而不能放到一个分区。(解决方法涉及覆盖技术)
(2)内存利用率低。任何程序即使很小,都需要占据一个完整的分区。例如:一个小于2MB的程序,当它被换入时,仍占据一个8MB的分区(假设分区大小为8MB)
大小不等的分区策略,最简单的办法就是把每个进程分配到能够容纳它的最小分区。
使用大小不等的分区可缓解大小相等的分区的缺点。但不能完全解决这两个问题
(1)分开队列:每个分区都需要维护一个调度队列,用于保存从这个分区换出的进程
(将进程比作车,分区比作车库。那么在分开队列中就会有不同的车型对应不同的车库)
弊端:,但是当还有空闲分区,但等待的程序不在该分区的等待列表上,就将造成有空间而不能运行程序的尴尬处境。
(小车库已经满了,大车库还有空闲,但是都在小车库那里排队。)
(例如:在某个确定的时刻,系统中没有12MB-16MB之间的进程。此时即使系统中有一些更小的进程本可以被分配到16MB的分区中,但16MB的分区将
仍会保持闲置)
(2)共享队列:为所有进程只提供一个队列(一般会用这个) 弊端:由于程序大小和分区大小不一定匹配,有可能形成一个小程序占用大分区的情况,从而造成内存里面虽然有小分区闲置,但无法加
载大程序的情况。
实现简单(分配回收容易),只需要极少的操作系统开销
(1)分区的数量在系统生成的阶段已经确定,因而限制了系统中活动(未挂起)进程的数量(即:动进程的最大数目是固定的)
(2)有内部碎片,对内存的使用不充分
分区长度和数量是可变的。当进程装入内存时,系统会给它分配一块与其所需容量完全相等的内存空间。
(1)没有内部碎片 因为动态分区中的分区是根据进程的需求来分配的,所以不会有内碎片(因为分区中不会有未被使用的剩余空间)
(2)对过程的大小没有限制 对比于固态分区的由于缺少足够的连续内存空间而使得大于分区大小得进程无法执行,动态分区中进程的大小是不受控制的。
(3)由于没有内碎片,所以更多得进程将会被加载到内存中
易产生外碎片
具体分析如下:(可看课本P200 图7.4 直观了解)
若一开始装入n个进程(此时在内存空间上是连续的),剩余空间为2MB(在内存空间的最后面),但是第n+1个进程的所需内存大于2MB(此时进程n+1无法获取内存空间)。而在某个时刻,内存中没有一个就绪队列,此时操作系统将换出内存中的某个进程m,而当进程m比进程n+1大时,则当第n+1个进程被换入内存时,在该进程与第m+1个进程中会形成外碎片…以此类推随着时间的推移,内存中形成越来越多的外碎片,内存利用率下降。
在固定分区中,分区列表只会生成一次,并且永远不会改变,但在动态分区中,分配和释放非常复杂,因为每次分配给新进程时分区大小都会发生变化。 操作系统必须跟踪所有的分区。
由于在动态内存分配中分配和取消分配的操作非常频繁,而且分区大小每次都会改变,所以操作系统很难管理所有内容。
(1)工作原理:该算法从空闲分区链首开始查找,直至找到一个能满足其大小要求的空闲分区为止。然后再按照进程的大小,从该分区中划出一块内存分配给进程,余下的空闲分区作为新的空闲区。
(2)优点:倾向于使用内存中低地址部分的空闲区,在高地址部分的空闲区很少被利用,从而保留了高地址部分的大空闲区。显然为以后大进程分配大的内存空间创造了条件。
(3)缺点:低地址部分不断被划分,留下许多难以利用、很小的空闲区(空闲区分布不均匀),而每次查找又都从低地址部分开始(高地址,会增加查找的开销。
首次适配的变种(首次适配算法每次都从链头开始寻找,这可能会导致低地址部分出现很多小的空闲区(即外碎片)、也会增加查找开销,从上次查找结束的位置开始检索,能解决上述问题)
(1)工作原理:空闲分区以地址递增的顺序排列,每次分配内存时从上次查找结束的位置开始检索空闲分区,直到找到满足要求的第一个空闲分区)
(2)优点:平均查找时间缩短,空间利用比FF(首次适配)均衡
(3)缺点:可能导致无论低地址、高地址部分的空闲分区都有相同的概率被使用,也就导致了高地址的大分区更可能被使用,而被划分为小分区,最后导致无大分区可用)
(1)工作原理:空闲区按分区大小递增顺序排列。分配时,从链首开始查找,第一个满足要求的空闲区(不同于别的就在这里
这里有限制条件)就是满足要求的最小空闲区,划分之(2)缺点:每次都选最小的分区进行分配,会留下越来越多的、很小的、难以利用的内存块。(即会产生很多外碎片)
(1)首次适配的性能最好(算法开销小,回收分区后,一般不需要对空闲分区队列重新排序);内存分配上,FF最快。(不需调整新空闲区位置) ; 内存回收上,FF最佳。(合并空闲区容易)
(2)最佳适配的性能最差(算法开销大,查找最合适空闲区时,BF最快,但碎片最多,压缩最频繁。回收分区后可能需要对空闲分区队列重新排序)
(3)空闲分区以地址递增的顺序进行排列。内存分区的大小发生了变化,并不需要对整个链表重新进行排序
(4)NF比FF更均衡地利用内存,碎片遍布内存,但使高地址端的大空闲区很快被划分,压缩次数多。
(1)页框(帧frame):内存划分为大小相等的物理块
(2)页面(页page):进程的逻辑地址空间划分为同样大小的逻辑块
为了使分页更加方便(容易地表示出相对地址),页/帧大小=2^m B(512B~16MB,通常为4KB)
(3)页表:记录进程的每页所对应页框的位置(每个进程有一张自己的页表;页表位于内存)
在内存中页框的使用情况,例如:
页表的表示:
OS维护空闲页框列表(常用位图表示):
(4)地址空间:时一个进程可用于寻址内存的一套地址集合
(5)物理地址空间:是指内存中物理单元的集合,它是地址转换的最终地址,进程在运行时指令和访问数据都要通过物理地址从主存中存取。当装入程序将可执行代码装入内存时,必须通过地址转换成物理地址,这个过程成为地址重定位。
(6)逻辑地址空间:当链接程序将各个模块接成一个完整的可执行目标程序时,链接程序按顺序依次按各个模块的相对地址构成统一的从0号单元开始编址的空间
(7)不同进程可以有相同的逻辑地址,因为这些相同的逻辑地址可以映射到主存的不同位置
解决碎片问题,允许进程的逻辑空间地址不是连续的
假如逻辑地址为32bit,页长为4KB(即2^12 B)
则逻辑地址可被表示为
(1)页长和页内偏移的字节数量的关系:根据页长来划分,假如页长为2^11,则页内偏移为11 B
(2)页内偏移字节数量和页号字节数量的关系:确定了页内偏移的字节数量之后 那在逻辑地址空间剩下的就是页号的字节数量
(3)地址位与表达的字节数量的关系:n位地址,可以表达的地址空间位2^n B
(4bit 二进制地址,能表示2^4B=16B)
(4)给定逻辑地址A,求所在页号p和页内偏移量d:页号p=INT(A/L) 页内偏移d=A mod L
例如:逻辑地址4100,页长4KB
[4100/4096]=1
4100%4096=4
所以页号p=1,页内偏移d=4。
步骤:
(1)按照逻辑地址上的页号到页表中找到那一也对应的页框的位置k,得到页框的物理起始地址k*2^m(其实就是在k后面加上m位的0,m是页内偏移量的位数)
(2)再将物理起始地址加上偏移量 就可以得到物理地址
更简单来说就是找到页框号之后,直接在后面拼接上页内偏移量(不需要计算)(可以回忆CSAPP)
页式地址转换的十进制例子:(重要)
每页长1KB,页表如下
逻辑地址1500,3500所对应的物理地址是多少?(其实和前面提到的求页号和页内偏移是有关系的)
解:1KB=1024B
页号=int(1500/1024)=1
偏移量=1500%1024=476
查表
页号1(即1#页)对应帧号4(即4#帧)
则物理地址=帧号*帧长+页内偏移=4*1024+476=4572
页号2=int(3500/1024)=3,超出页表,越界
为进程的多个页离散地分配内存帧。每个进程在内存中浪费的空间仅是进程最后一页的一小部分形成的内部碎片,无外碎片,内存利用率高。
分页将造成进程上下文切换的一个开销,进程被分配CPU后,每次CPU都需要调入页表
课后习题7.12:页式内存管理系统。内存大小为2^32B, 页大小为2的10次方B,逻辑地址空间包含2的16次方页。
题目分析:内存大小,说明物理地址的长度位32位;页大小,说明页内偏移的长度为10位;页数,说明页号为16位
c. 物理地址中有多少位是页框号?
d. 页表中最多有多少表项?
e. 设每个页表项包含一位“有效位”,则每个页表项有多少位?
采用分段技术,将程序和与其相关的数据划分到几个段中。(段长不固定,每段有自己的段号,每段都
是从0开始编址。)
与动态分区不同的是,在分段方案中,一个程序可以占据多个分区,并且这些分区不要求是连续的。
由两部分组成:段号(s)和偏移量(d)
每个进程都有一个段表,系统也会维护一个内存中的空闲块列表。每个段表项必须给出相应段在内存中的起始地址,还必须指明段的长度(尽管段有最大长度限制,但并不要求所有程序的所有段的长度都相等),以确保不会使用无效地址。当进程进入运行状态时,系统会把其段表的地址装载到一个寄存器中,由内存管理硬件来使用这个寄存器。
(1)段表:
(2)硬件支持
段表基址寄存器:根据此地址找到段表。
段表长度寄存器:当段号s<段表长度时,段号s合法。如果段号s≥段表长度,则越界
(3)段式地址转换
在段表中查找段号,得到相应段的内存起始地址;
段内偏移量和该段长度比较,如果段内偏移量≥段长,则越界,这个逻辑地址无效。
物理地址=段起址+段内偏移量。
(1)共享保护容易。 (2)无内碎片(但和动态分区一样,会产生外碎片。不过由于进程被分为多个小块,因此外碎片也会很小)
分页 | 分段 |
---|---|
信息的物理单位 | 信息的逻辑单位 |
页长由系统确定 | 段长是任意的 |
页框起始地址只能以页框大小的整数倍开始 | 段的起始地址可以从主存任意地址开始 |
(页号、页内位移)构成了一维地址空间 | (段号、段内位移)构成了二维地址空间 |
消除了外部碎片,但会出现内部碎片 | 会产生外部碎片 |
内存管理是操作系统中最重要、最复杂的任务之一。内存管理把内存视为一个资源。他可以分配给多个活动进程,或由多个活动进程共享。为有效使用处理器和I/O设备,需要在内存中保留尽可能多的进程,此外,程序员在开发时最好能不受程序大小的限制
内存管理的基本工具是分页和分段。采用分页技术,每个进程被划分为较小的、大小固定的页。采用分段技术可以使用大小不同的块,在单独的内存管理方案中,还可结合使用分页技术和分段技术。