操作系统中管理分成存储器的部分成为存储管理器。他的任务是有效地管理内存,即记录哪些内存是正在使用的,哪些内存是空闲的;在进程需要时为其分配内存,在进程使用完后释放内存。
无存储器抽象
最简单的模型,及存储器模型就是物理内存,不过有三种变体:
- 操作系统位于RAM的地步
- 操作系统位于内存顶端的ROM
- 设备驱动程序位于内存顶端的ROM,而操作系统中其它部分位于下面的RAN的底部
第一种和第三种方案的确定是用户程序出现的错误可能摧毁操作系统(RAM)
在没有内存抽象的系统中实现并行的一种方法是使用多线程编程。更进一步,一个没有内存抽象的系统也不大可能具有线程抽象的功能。
程序直接使用绝对物理地址是最需要避免的。
不过在收音机,洗衣机等设备就完全被(ROM形式)软件控制,这些软件都采用访问绝对内存地址的寻址方式。
一种存储器抽象:地址空间
将物理地址暴露给进程会导致一下严重问题:
- 如果用户程序可以寻址内存的美国字节,则可以很容易的破坏系统,泵式系统慢慢停止运行(除非有硬件保护),这个问题在单用户使用时也存在
- 单CPU同时运行多个程序是非常困难的
地址空间概念
要保证多个应用程序同时处于内存中并且互不影响,则需要解决两个问题——保护和重定位。
地址空间是一个进程可用于寻址内存的一套地址的集合。每个进程都有一个自己的地址空间,并且这个地址空间独立于其他进程的地址空间(除了共享空间)
很多CPU中都有两个特殊的寄存器——基址寄存器和界限寄存器,当一个进程运行时,程序的起始物理地址装载到基址寄存器,程序的长度装载到界限寄存器。
每次一个进程访问内存,取一条指令,读或写一个数据字,CPU硬件会把地址发送到内存总线前,字段吧基址值加到进程发出的地址值上,同时,他坚持程序提供的地址是否等于或大于界限寄存器里的值,如果访问的地址超出界限,则会产生错误终止访问。
在很多实际系统中,对基址寄存器和界限寄存器会以一定的方式加以保护,使得只有操作系统可以修改他们。
使用基址寄存器和界限寄存器重定位的缺点是——每次访问内存都需要进行假发和比较运算,比较可以做的很快,但加法由于今晚传递时间的问题,在没有使用特殊电路的情况下速度较慢。
交换技术
有两周处理内存超载的通用办法,最简单的策略是交互技术,即吧一个进程完成调入内存,是该进程运行一段时间,然后把它存回磁盘。空闲进程主要存储在磁盘上。
另一种则为细腻内存,该策略使程序在只有一部分被调入内存的情况下运行。
由于交换产生的位置变化,在换入式可以通过引进对其地址进行重定位。
交换在内存中会产生多个空闲区,吧所有进程尽可能向下移动,有可能将这些空闲区合成一大块,这种技术成为内存紧缩。这种操作很少进行,因为耗费时间漫长。
若一个进程在内存中不能增长,而且磁盘上的交换区也已满了,那么这个进程只有挂起直到一些空间空闲(或者可以结束进程伤害)
若干大部分进程在运行时都要增长,为了减少内存区域不够而引起的进程交换和移动所产生的开销,一种可用的方法是,当还入或移动进程时为它分配一些额外的内存。不过当进程被换到磁盘上时,不需要交换出额外的内存。
如果进程有两个可增长的段,那么放在中间,堆栈段在进程所占内存的顶端并向下增长,紧接在程序段后面的数据段向上增长。
空闲内存分配
在动态分配内存时,操作系统必须对其进行内存管理。一般而言,有两种方式可以跟踪内存使用情况——“位图和空闲链表。
在使用位图时,内存可能被划分成或大或小的分配单元,位图中的一位代表一个分配单元。
因为内存的大小和分配单元的大小决定了位图的大小,所以它提供了一种简单的利用一块固定大小的内存区就能对内存使用情况进行记录的方法。这种方法主要的问题是在觉得一个站K个分配单元的进程进入内存时,存储古娜里奇必须搜索位图,哎位图中找出有k个连续0的字符串。查找位图中指定长度的连续0串是耗时的操作(因为可能跨越字的边界),这是位图的缺点。
而另一种方法则是维护一个记录已分配内存段和空闲内存段的链表,其中链表中的一个阶段或者包含一个进程,或者是两个进程之间的一个空的空闲区。
因为进程表中表示终止进程的节点中通常含有执行对应于其段链表节点的指针,因此段链表使用双链表往往比单链表方便,这样更容易找到上一个节点。
段链表常见算法:
- 首次适配算法(找到第一个足够大的空间)
- 下次适配算法(和1相同,不过找到空闲区会记录位置,下次从结束的位置开始搜索,而不是想首次适配那样从头开始,不过性能甚至略低于首次适配)
- 最佳适配算法(找到最合适的,每次都会搜索整个链表,因此较慢,在内存利用率上也低,因为产生大量无用的小空间)
- 最差适配算法(每次总分配最大的,然而依旧不佳)
如果为进程和空闲区维护各自独立的链表,那么四种算法的速度都可以得到提高,但是代价为增加复杂度和内存释放四度变慢,因为必须将一个回收的段从进程链表中删除而插入到空闲链表中
如果进程和空闲区使用不同的离那边时,则可以可以按照大小按空闲区链表排序,以便提高最佳适配算法的速度。
单独联邦不必用单独的数据结构存储这些信息,而是可以利用空闲区存储这些信息。
还有一种算法成为快速适配,它为那些常用大学的空闲区维护单独的链表,比如第一个4kb,第二个8kb,第三个16kb。。。其查找指定大小的空闲区速度极快,但是它和所有将空闲区按大小顺序排序的算法一样存在着一个确定——当进程终止或被换出时,寻找它的想link,查看是否可以合并的过程非常费时,而不合并则会迅速产生大量无法利用的小空闲区。