存储器管理
存储器的层次结构
程序的装入和链接
用户程序要在系统中运行的话,就必须装入内存,然偶后将其转变为一个可执行的程序。
- 编译
由编译程序对用户源程序进行编译,形成若干个目标文件
- 链接
由链接程序将编译后形成的一组目标模块以及它们所需要的库函数接在一起,形成一个完整的装入模块
- 装入
由装入程序将装入模块装入内存
程序的链接
- 静态链接方式
在程序运行前,就先将各个目标模块以及它们所需要的库函数链接成一个完成的装配模块,之后都不会拆开,这样需要解决两个问题:修改相对地址,变换外部调用符号
- 装入时动态链接
在用户源程序编译后得到的一组目标模块,在把目标模块装入内存时,采用边装入边链接的方式,在转入目标模块时,若发生一个外部模块调用事件,将引起装入程序去找出相应的外部目标模块,并将其装入内存,也需要修改目标模块中的相对应的地址
- 运行时动态链接
在执行过程中,当发现一个被调用模块没有装入内存时,立即由OS去找到该模块,并将之装入内存中,将其链接到调用者模块上去
程序的装入
- 绝对装入方式
当计算机系统很小,且仅能运行单道程序,完全有可能知道程序将驻留在内存的什么位置,此时可以采用绝对装入方式,用户程序经过编译后产生绝对地址
- 可重定位装入方式
在多道程序环境下,编译程序时不可能知道编译后的模块地址放在在内存的什么地方,它们的起始地址时候从0开始,程序的其他地址是相对于起始地址计算的,所有要采取可重地位方式,根据内存的情况装入到内存中
- 动态运行时的装入方式
可重定位装入方式虽然可以将模块装入到内存中任意允许的地方,但是不允许程序在内存中移动位置,因为在具有对换功能的系统,一个进程可能会被多次换进换出,所有采用动态运行的装入方式会比可重定位的转入方式好,动态运行时装入并不会立即把装入模块的逻辑地址转换为物理地址,而是在程序真正运行的时候才会进行转换,这种方式需要一个重定位寄存器的支持
程序装入内存的问题解决了,但是内存又是怎么为程序分配内存的?
分配存储管理
连续分配
- 单一连续分配
在单道程序环境下,内存分为系统区和用户区,系统区只留给OS时候,放在低地址部分,用户区仅装有一道用户程序。
- 固定分区分配
将内存的用户空间划分为若干个固定大小的分区,,并为之建立一张分区使用表,包含每个分区的起始地址,大小,状态
- 动态分区分配
要实现动态分区分配,必须解决分区所用的数据结构,分区分配算法和分区的分配与回收这三个问题。
- 数据结构
- 空闲分区表
- 空闲分区链
- 分配算法
- 基于顺序搜索
- 首次适应(FF)算法
从链首开始顺序查找,知道找到适合空闲分区大小,从中划分进程所需要的内存空间,每次查找都是从低地址部分开始
- 循环首次适应(NF)算法
和首次适应算法差不多,但开始位置不再是从链首开始,而是从上次找到的空闲分区额下一个空闲分区开始
- 最佳适应(BF)算法
该算法要求将所有的空闲分区按其容量从小到大的顺序形成一个空闲分区链表
- 最坏适应(WF)算法
扫描整个空闲分区表,总是挑选一个最大的空闲区,从中分割一部分存储空间给进程使用。
- 首次适应(FF)算法
- 基于索引搜索
- 快速适应算法
将空闲分区根据容量大小进行分类,对于每一类具有相同容量的所有空闲分区,单独设立一个空闲分区链表
- 伙伴系统
该算法规定无论已经分配分区或是空闲分区,大小都为2的K次幂,当2的i次幂的空闲分区不够用的时候,会将2的i+1次幂的空闲分区拆分为两个相等的2的i次幂分区,这两个分区就称为一对伙伴
- 哈希算法
- 快速适应算法
- 基于顺序搜索
- 分配操作
- 分配内存
事先规定最小的剩余分区大小size,当需要分配u.size大小内存的时候,从空闲分区表(链)中寻找到分区大小m.size,如果,u.size-m.size>size,那么把剩余的空闲大小划分出来,否则就直接全部分配。
- 回收内存
回收内存有四种情况:
- 回收区与插入点的前一个空闲分区F1相邻,此时只需将回收区和插入点的前一个分区合并,不需要为回收区分配新表项,只需要修改前一个分区的F1的大小(图a)
- 回收区与插入点的后一个空闲分区F2相邻,分区合并,用回收区的首地址作为新空闲区的首地址,大小为两个之和。(图b)
- 回收区同时与插入点前后两个分区邻借,将三个分区合并,取消F2的表项,大小为者之和(图c)
- 回收区既不与F1相邻,与不与F2相邻,这是要为回收区单独建立一个新表项,填入回收区的首地址和大小
- 数据结构
- 动态可重定位分区分配:需要一个重定位寄存器
在动态分区分配方式中,内存经过一段时间的分配回收后,内存中存在很多很小的空闲块。它们每一个都很小,不足以满足分配要求;但其总和满足分配要求。这些空闲块被称为碎片。造成了存储资源的浪费。
碎片问题的解决: 紧凑技术(拼接):通过在内存中移动程序,将所有小的空闲区域合并为大的空闲区域。每次“紧凑”后,都必须对移动了的程序或数据进行重定位。
分配算法
离散分配
- 分页存储
页面:分页存储管理将进程的逻辑地址空间分为了若干个页,并为各个页加以编号,这个页叫做页面,页面大小为2的幂,通常为1KB~8KB
物理块:在内存的物理地址空间也分为了若干个块,称为物理块
地址结构:它包含两个部分,前一部分为页号,后一部分为位(偏)移量,即页内地址
页表:为了完成页号到物理块号的地址映射,采用了页表。
快表:为了提高地址变换速度,增加了一个具有并行查找能力的高速缓冲寄存器,这个寄存器叫做快表或者TLB,有了快表之后,当快表中有CPU需要的内存地址页号时,直接从快表中获取,不需要访问内存中页表
- 分段存储
段:将地址空间分为若干个段,每一个段定义一组逻辑信息,段的长度并不一定相等,逻辑地址由段号和段内地址组成
段表:和页表一样的,是一张段映射表
分段存储和分页存储的区别:
- 页是信息的物理单位,段是信息的逻辑单位
- 页的大小是固定的,由操作系统决定,而段的长度是不确定的,决定于用户所编写的程序。
- 分页的用户程序地址空间是一维的,而分段系统中,用户的地址空间是二维的,程序员在标识一个地址时,需要给出段名,有需要给出段内地址。
- 段页式存储
基本原理:先将用户程序分成若干个段,再把每一个段分成若干个也,并为每一个段赋予一个段名
地址结构由段号,段内页号,页内地址三部分组成