【11】存储管理

一、概述

内存:存取速度快、存储容量大和成本低(理想情况)

1、存储分层结构

存储分层结构Cache:高速缓存,多由硬件控制,可以暂存内存数据,速度比内存快,容量有限
内存:OS存储管理主战场,速度快,容量大,但信息断电消失,不能长久保存大容量数据
外存:磁盘等可以永久保存信息的大容量存储介质,成本也较低

2、要解决的问题

2.1 存储保护

内存是以字节或字作为基本存储单元进行编址的,每个存储单元对应一个地址,由这些连续的地址就构成了存储空间,程序计数器PC所真正指向的位置

OS将内存分为系统区和用户区,系统区用于存放OS,用户的程序和数据只能放入用户区,每个用户进程在内存都有自己的存储空间,不允许用户进程读写不属于自己进程的内存单元

为防止地址越界,硬件提供一对寄存器:

  • 基址寄存器:存进程的起始地址
  • 限长度寄存器:存进程的长度
2.2 地址重定位

地址重定位:程序需要运行,需要将处于外存的程序装入内存(在外存可装入模块中的偏移地址编程变成内存的地址),让程序计数器PC指向开始地址

  • 逻辑地址(相对地址,虚地址):不能用逻辑地址直接在内存中读取信息;
  • 物理地址(绝对地址,实地址):PC所指向的内存中的地址;
  • 地址重定位(地址变换,地址映射):将逻辑地址变换为物理地址
    在这里插入图片描述
  • 编译阶段:将程序翻译成机器语言的目标模块,不分配内存,只根据各种符号声明时的类型去占位,完成从符号名到目标模块内偏移地址的转换;
  • 链接阶段:程序编译后的形成的各个目标模块通过链接程序形成可装入模块;
  • 装入阶段:将可装入模块加载进内存;
程序的链接
  • 静态链接:将目标模块和库函数链接成一个完整的可装入模块,形成整个程序的连续逻辑地址;
  • 装入时动态链接:对编译形成的一组模块边装入、边链接,各模块独立分开装入内存的不同位置,便于修改,便于共享;
  • 运行时动态链接:边运行、边链接,不会运行到的模块(如错误处理)不必装入,节省内存
程序的装入
  • 绝对装入方式:编译程序产生绝对地址的目标代码,将可装入模块放入内存指定的位置;(嵌入式系统)
  • 可重定位装入方式(静态重定位):装入阶段一次性的完成地址变换,程序装入时,根据其在内存中的起始地址(基地址),将其中所有的逻辑地址转变为物理地址
  • 动态运行时装入方式:程序在内存中的位置可以改变,边运行,边定位,程序运行时,根据其在内存中的起始地址(基地址),将其中所有的逻辑地址动态计算出物理地址
2.3 内存扩充
  • 物理扩充:受条件限制
  • 逻辑扩充:编程可用的地址空间(虚拟)远远大于存储空间

二、内存连续分配方式

内存连续分配:为一个用户程序分配一块不小于指定大小的连续物理内存空间

1、单一连续分配

单一连续分配:出现在批处理时代,系统区通常在低地址部分,用户区在任一时刻只连续的存放一道用户作业,优点是简单,易于管理,但仅适用于单用户、单任务的OS,而且容易造成内存浪费

2、分区式连续分配

分区式连续分配:用户区被分成一些大小相等或不等的部分供用户进程使用

2.1 固定分区分配

固定分区分配:一个用户进程只占据一个分区,当程序要装入到内存运行时,若有合适大小的空闲分区则装入内存,否则等待;OS可通过分区表或分区链表来实现存储管理

  • 分区大小限制了进程大小,分区个数限制了并发进程个数
  • 内部碎片浪费内存空间
2.2 可变分区分配

可变分区分配:当进程装入内存时,为进程分配指定大小的分区,OS通过可变分区表和空闲区链表来管理分区

  • 首次适配算法(First Fit):空闲分区按地址递增的顺序排列,时间性能较好,但随着低地址分区不断划分,会产生较多小分区(外部碎片),影响内存的利用率和查找开销
  • 循环首次适配算法(Next Fit):在首次适配的基础上,从上次内存分配之后的分区开始查找可用分区
  • 最佳适配算法(Best Fit):空闲分区按容量递增的顺序排列,会形成较多的外部碎片,集中在空闲分区表的起始部分的小分区会降低性能
  • 最差适配算法(Worst Fit):空闲分区容量递减的顺序排列,但较大的空闲分区不被保留
2.2 可变分区回收(分区合并)
  • 上邻接:回收分区与低地址空闲区相邻
  • 下邻接:回收分区与高地址空闲区邻接
  • 上、下均邻接
  • 上、下均不邻接:需要在分区表中增加一个新回收的分区信息

三、分页存储管理

1、地址转换

分页存储管理MMU(存储管理单元):把虚拟地址转化为物理地址

在这里插入图片描述

2、地址空间划分

在逻辑地址空间划分成大小相等的区域,称为页;
在物理地址空间划分成大小相等的区域,称为块

逻辑地址结构:页号 + 页内偏移量(x86页面大小是4kB)

页表:存放逻辑地址的页号与物理空间的块号之间的关系(OS负责填写这个页表)

基本地址变换结构
页表寄存器(页表始址 p0 ,页表长度 l)
基本地址变换结构


四、快表和两级页表

1、通过页表访问内存数据

通过页表访问内存数据:两次访问内存(效率低)

  • ① 访问内存中的页表,通过页表取得对应内存物理块的块号
  • ② 访问内存中的数据

在这里插入图片描述

2、快表(联想寄存器)

快表(联想寄存器)快表TLB:将最近频繁访问的页表项放在快表中

命中率:快表中成功查找到所需页表项的比率(快表命中率可达90%以上)
访问内存的有效时间(EAT):从进程发出指定逻辑地址的访问请求,到最终从实际物理内存单元取出数据的时间

利用快表进行地址变换
利用快表进行地址变换

3、两级页表

只保存用到的页表项,解决页表占用存储空间的问题
外层页表:记录各个内层页表所在物理块号的外层页表
内层页表:保存一部分页表
两级页表

  • 对大页表进行分页
  • 解决大页表占用大的连续存储空间的问题,提高了空间效率
  • 增加了访存时间,可通过快表进行补偿

五、分段存储管理

分段存储管理

1、满足用户的需求:

  • 方便编程:用户在访问数据时可以根据逻辑关系指出,逻辑地址由段号 + 段内地址构成
  • 信息共享:程序和数据的共享是以信息的逻辑单位为基础的,而分页系统中的页只是存放信息的物理单位块,段却是信息的逻辑单位(共享段)
  • 信息保护:程序分段保存,各段保持自己的特点
  • 动态增长:程序每段分别装入内存(高效利用内存)
  • 动态链接:动态链接以段为单位进行链接

2、分段存储管理基本原理

内存分配:以段为单位分配内存,每个段在内存中占据连续的空间,但各段之间可以不连续存放(首次适配、最佳适配、最差适配)

内存空间划分:内存空间被动态的划分为若干个长度不等的物理段,每个物理段由起始地址和长度确定

内存空间管理:记录空闲区起始地址和长度

段表:每个进程一个段表,放在内存中,在进程PCB中会保存段表的起始地址和段表项的个数

3、地址变换

地址变换

4、分页和分段的主要区别

  • 页是物理单位,分页是为了消减内存的碎片以提高内存的利用率,仅仅是系统需要;
    段是逻辑单位,分段是为了更好的满足用户需要
  • 页的大小固定且由系统确定,分页由硬件实现
    段的长度不固定,由编译时根据程序信息来划分
  • 分页的逻辑地址空间是一维的线性空间,标识地址时,只需给出一个逻辑地址
    分段的逻辑地址空间是二维的,标识地址时,必须给出段号和段内地址

5、段页式存储管理

满足用户要求,提高内存利用率

段页式存储管理

基本原理:

  • 用户程序划分:按段的逻辑关系进行划分
  • 内存划分:按页面进行划分
  • 内存分配:对用户程序的每个段,在内存中以页为单位进行分配

逻辑地址:
段页式


六、虚拟存储器

虚拟存储器:具有请求调入功能置换功能,能从逻辑上对内存容量加以扩充的一种存储器系统,其逻辑容量系统由内存容量和外存容量之和所决定(实际容量由地址寄存器决定),其运行速度接近于内存速度,而每位成本却又接近于外存

1、为什么引入虚拟存储器?

作业必须全部放入内存后方可运行

  • 如果作业大于内存的容量
  • 如果有大量作业要求运行,内存不足以容纳

从逻辑上对内存进行虚拟扩充,由硬件和操作系统合作实现虚拟扩充

2、常规存储管理方式

  • 一次性装入
  • 运行时的驻留性

3、程序局部性原理

  • 时间局部性:指令被执行,在不久可能再被执行
  • 空间局部性:存储单元被使用,一定时间内临近单元可能被使用

4、虚拟存储器的基本工作情况

  • 只把部分程序放到内存中,其他部分留在外存
  • 需要时在内存与外存之间动态对换

以处理机时间和外存空间来换取内存空间的资源转换技术

  • 离散性:分页、分段
  • 多次性:多次将部分调入
  • 交换性:暂时不执行时允许换出,需要时换入
  • 虚拟性:逻辑(虚)上扩充了内存物理(实)容量

5、虚拟存储器的实现方式

请求分页方式
请求分段方式

硬件支持:地址转换机制

  • 请求分页(段)的页(段)表机构
  • 缺页(段)中断处理机构
  • 请求分页(段)的地址变换机构

OS:管理内存和外存间页面或段的请求调入和置换


七、请求分页存储管理

1、 请求分页的页表机制

请求分页的页表机制

  • 状态位:表示该页是在内存还是在外存
  • 访问位:记录该页在一段时间内被访问的次数
  • 修改位:查看此页是否在内存中被修改过(如修改,必须将该页重写回外存上)
  • 外存地址:该页在外存上的地址(通常是物理块号)

2、 缺页中断处理机构

缺页中断处理过程:页表状态位 -> 产生缺页中断 -> 操作系统 -> 缺页中断处理程序 -> 页表外存地址 -> 调入页面

和中断的区别:

  • 缺页中断在指令执行期间产生和处理中断信号;
    CPU则通常是在一条指令执行完后才去查看有无中断请求
  • 一条指令在执行期间可能产生多次缺页中断

3、 地址变换机构

地址变换机构

4、页面内存分配和调入策略

内存分配中OS提供的支持:

  • 请求调页时,把所需的页从外存调入内存
  • 置换时,将内存的某些页调至外存

内存分配问题:

  • 进程正常运行所需的最少物理块?
    与计算机硬件结构有关,主要取决于指令的格式、功能和寻址方式
  • 每个进程分配的物理块数是固定的吗?
    通常有固定分配局部置换、可变分配局部置换、可变分配全局置换
  • 每个进程分配的物理块数依据是什么?
    平均分配、按比例分配

页面调入

  • 何时调入:预调入、请求调入
  • 何时调入:对换区、文件区
  • 怎样调入:内存满、内存空

5、影响缺页率的因素

  • 分配给进程的物理块数
  • 页面本身的大小
  • 程序的编制方法
  • 页面置换算法

八、页面置换算法

1、页面置换算法的概念

页面置换算法:请求分页存储管理系统中,当需要调入不在内存的页面,但内存已无空闲空间时,从内存选择换出页面的算法。置换算法的好坏直接影响到系统的性能

选择置换页的原则:好的页面置换算法应具有低的页面更换频率(缺页率)

2、常见页面置换算法

2.1 最佳置换算法(OPT)

淘汰永不使用或最长时间内不再被访问的页,无法实现,只能用它做评价标准

2.2 先进先出置换算法(FIFO)
  • 简单易行
  • 没有考虑访问频度的差别,不能保证经常访问的页不被淘汰

异常:抖动现象(刚淘汰的页面又要用到,重新装入内存要淘汰页面,被淘汰的页面又需要访问)

2.3 最近最久未使用置换算法(LRU)

淘汰在最近最久未使用的页面

硬件支持

  • 特殊栈:保存当前在内存的各页面号,当前访问页号始终保持在栈顶,栈底就是最近最久未使用的页号
  • 移位寄存器:为每个在内存的页面配置一个移位寄存器,当访问某页时将相应的寄存器最高位置1,每隔一段时间将寄存器右移1位
2.4 简单的CLOCK置换算法

(循环队列)每页设置一个访问位A,当某页被访问时,其访问位A被置为1
在循环检查时,如果页的访问位A=0,则淘汰该页,若A=1,则重新将A置为0,暂不换出而给该页第二次驻留内存的机会

3、Linux中虚拟内存的统计

vmstat用来展示虚拟内存的统计情况,如 vmstat 5 5 表示在5秒内进行5次采样
Linux中虚拟内存的统计swpd:使用虚拟内存大小
free:可用内存大小
buff、cache:缓冲缓存的内存大小
swap:每秒从交换区写到内存的大小和写入交换区的内存大小


九、可执行文件与进程虚拟地址空间

1、Linux进程虚拟地址空间(x64)

Linux进程虚拟地址空间(x64)实际的64位系统只使用了48位,可寻址的地址空间是2^48个字节,256TB,Linux使用其中的128TB作为内核空间,128TB作为用户空间,内核空间与用户空间之间是一个巨大的空洞(这部分区域不使用)

用户空间内存布局:保留区、代码段、数据段、BSS段、堆、内存映射段、栈

  • 一个可执行程序包含二进制指令(映射到代码段中)和待处理的数据(映射到数据段和BSS段中),初始化过的变量保存在Data段上,没有初始化过的变量保存在BSS段上,Data段和BSS段都属于静态存储区,静态存储区用来保存全局变量和静态变量

  • 堆中用来保存使用malloc申请分配的内存

  • 内存映射段用来映射动态链接库的文件

  • 栈中保存自动变量(如main中定义的局部变量,函数的调用关系等)

2、Linux实验

2.1 进程地址空间布局

进程地址空间布局每一行都对应着一个vma,用vm_area_struct结构体来描述,通过双链表进行组织,每一个vma都有起始地址、结束地址、权限(r:可读 x:可执行 w:可写 p:私有,不被共享)

未标出文件名和路径属于匿名映射

OS装载可执行文件时并不关心段中存放哪些内容,关心的是这些段的权限

OS进程地址空间布局Segment:段
Section(.data .bss等):节

readelf命令可查看节的信息
链接器在链接时会将相同属性的Section放在同一空间,合并为一个Segment

readelf命令可查看段(程序头)的信息
只有LOAD类型的Segment需要被映射,Segment里面包含着许多Section

2.2 ELF文件装载过程

OS装载时会将可执行文件中的Segment映射到虚拟地址空间中对应的VMA中
Segment映射到虚拟地址空间中对应的VMA

为什么没有BSS段的VMA?
在实际映射过程中,分配给Data段的内存空间大于自身的大小,多余的空间分配给BSS段,然后将BSS段全部填充为0,这样做的好处是并不需要为BSS段设立一个专门的Segment来映射,BSS段合并到Data段中映射

2.3 程序运行结果

ELF可执行文件中有3个Segment(可读可执行、只读、可读可写),需要关注的是4个Section(.text .rodata .data .bss),这四个Section与程序相关,其他部分在程序装载起辅助作用

ELF可执行文件

  • .text:存放程序可执行二进制代码
  • .rodata:只读数据,存放被const修饰的全局变量
  • .data:保存全局变量和静态变量,存放已初始化过的
  • .BSS:保存全局变量和静态变量,存放未初始化过的

在实际运行过程中,.text .rodata .data .bss的地址空间是相对固定的,但堆空间和栈空间的地址是会变动的
程序运行结果

  • A:未初始化的全局变量,存放在.bss上
  • B:初始化为0(BSS段会全部填充为0),效果与A(默认初始化为0)相同,存放在.bss上
  • C:初始化非0的全局变量,存放在.data中
  • D、E、F:static修饰,只是表示作用域是在本文件内,不影响内存分布情况(与A、B、C相同)
  • G、H:const修饰,变量内容不能改变,分配在.rodata
  • a、b、c:自动变量,分配在栈上
  • d、e、f:static修饰,静态变量,静态存储区(与A、B、C相同)
  • g:const修饰,动态变量,分配在栈上
  • char1:局部字符数组,自动变量,分配在栈上
  • *cptr:字符串指针,保存字符串地址,自动变量,分配在栈上(常量的存储是以ASCII码进行存储)
  • *heap:malloc申请地址空间,分配在堆上

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值