操作系统-课堂笔记-内存管理(南航)
文章目录
- 内存管理
- 回顾
- 内存管理的作用是什么?
- 如何分配物理内存
- 物理内存分配方案
- 1.连续分配存储管理(可应用于嵌入式设备)
- 1.1单一连续分配
- 1.2固定分区分配
- 1.3可变分区分配
- 连续分配存储方案总结
- 2.分页存储管理
- 概念补充
- 如何管理离散页?
- 实例练习
- 分析上述方案的优缺点
- 问题1:
- 问题2:
- 3.多级页表
- 二级页表如何将逻辑地址转换成物理地址?
内存管理
操作系统内核的几大模块:
进程调度是核心模块,本系列博客的大部分内容都在讲解进程调度模块。从本篇开始讲解内存管理,可能分成两篇来讲解。
回顾
进程的内存抽象:
还是再讲解下这张图:
- 每个进程有text段:存放代码
- data段:存放全局变量和静态变量
- heap区:new, malloc从此处申请空间
- stack区:存放局部变量
- 栈从高地址向低地址扩展
加过内存条吗?内存条长啥样?
内存条就长这样,当然还有一个问题:
- 早期的计算机内存很小很小,比如1991年Linux0.11版内核只能管理16M的内存空间,那么内存如果不够用怎么办?
- 有句话说的好,饭不够汤来凑:内存不够磁盘来凑。这里涉及虚拟内存,参见我的另一篇博客。
- 关于虚拟内存:饭不够汤来凑,饭和汤的区别是什么?
- 饭顶饱,汤不顶饱 对叭
- 那么内存和磁盘的区别呢?
- 同理,内存块,磁盘慢
磁盘长啥样?现在主要有两种:SSD(固态)和HHD(机械)
左边的是固态(笔者在更换大容量SSD的时候拍的),右边的是机械,机械硬盘俗称自行车(因为比较慢)。
现在的SSD降价很厉害,所以很多童鞋们都准备丢掉自行车啦!
好了,不闲扯了,步入正题
内存管理的作用是什么?
- 我们上面的第一张图给出了进程的内存抽象表示,也就是说进程创建的时候,需要获取一段内存空间,从而建立自己的各个段。
- 后面的几张图给出了内存条和磁盘的图片,那么问题来了,怎么在这两者之间建立联系?
- 这就是内存管理所起的作用,将内存和磁盘组成的内存空间 分配 给进程使用,如何分配就是我们要讲解的重点。
如何分配物理内存
下面以提问-回答的方式来思考如何分配物理内存!
首先要考虑一个问题,能不能把所有内存都给用户?
- 当然不能,想想我们开机后内存剩余多少?
- 笔者电脑开机后,内存已经被占用了近4G,这4G的内存去哪里了?
- 大部分给操作系统使用了(当然也有一个开机启动的软件占用了一部分内存),即给内核使用。
课外充电站:
Linux0.11版内核中一共可以管理16M内存,其中0-1M给内核使用,其余才能分配给用户
既然内存分成两部分:内核使用部分、用户使用部分,那么它们是否是平等的呢?
- 不平等,试想如果是平等的:那么我们可以随意修改内核所用的内存,那么系统岂不是乱套啦!
- 既然不平等,就要引入保护机制,防止恶意修改。
再来一个问题:我们如何记录哪些内存已经分配/未分配呢?这些信息保存在哪里?
- 衍生问题:新的请求到达时如何分配内存给它?
- 进程运行结束如何回收内存?
有了上述问题,我们开始研究内存分配方案。
物理内存分配方案
有以下几种物理内存分配方案:
- 连续分配存储管理
- 分页存储管理
- 多级页表管理
- 虚拟存储管理
- 分段存储管理
我们本应该先讲分段再讲分页,但是连续分配和分页机制是思维的一个转变,具有衔接性,所以我们讲完连续分配就讲分页管理。
1.连续分配存储管理(可应用于嵌入式设备)
连续分配存储管理可以再细分成如下几类:
- 单一连续分配
- 固定分区分配
- 可变分区分配
1.1单一连续分配
单一连续分配是最简单的管理方式:
- 只能用于单用户、单任务的操作系统
- 将可分配内存全部分配给用户进程
- 逻辑地址=物理地址
- 如果没有保护机制,用户进程可以修改OS内存,如MS-DOS
举个例子:
- 既然其适用于单用户、单任务的操作系统,那么同一时刻只能有一个进程在运行。
- 假设有两个任务:A、B,。A先运行,将所有内存都给A(比如1M-10M),需等A运行完或强行终止才能运行B。运行B时,将内存都给B(比如1M-9M)。
- 此即单一连续分配的基本原理。
优点:
- 简单
- 基本不需要硬件支持
缺点: - 单任务。我们使用的Windows都是分时多任务(写博客的时候也能听歌的)
1.2固定分区分配
将内存划分成固定大小的区域,每个区域装一个程序,举例如下:
- 第一个分区12KB大小
- 第二个分区32KB大小
- 第三个分区64KB大小
- 第四个分区128KB大小
优点:
- 简单、支持多道程序(上例中可以支持4道程序)
- 开始支持内存保护
- 是一种很好的思路,后面的分页思想也是借鉴了这种思路
缺点:
- 分区大小不好定,因为我们不知道要运行的程序有多大
1.3可变分区分配
是上述方案的改进版本,分区大小是可变的,这种方案最致命的缺点是容易形成小碎片,至于碎片如何形成,下面举例讲解。
这里的可变分区看似很灵活,比如某进程需要10M内存,那么就分配给它10M内存,这种灵活的背后隐藏着问题:
如何分配内存以及小碎片如何形成:
- 开始时内存是完整一块,可以理解成一个hole
- 某进程请求内存->从hole中选取一段空间分配给它
- 某进程执行完毕,释放hole
如上图所示: - 开始三个进程将内存占满了
- 然后process8执行完毕,将内存空间释放
- process9进入,从空内存中分出一段给它
- 随后process10进入,同理,分一段给它
- 最后剩余一小段空间,假设这小段空间很小很小,以至于不够任何进程使用,那么这段小碎片就浪费掉了
- 同理:按照上述思路无数次分配释放之后会形成很多很多小碎片,这些小碎片太小了,用不起来
所以我们需要引入额外的机制来解决上述的小碎片问题:
首先我们思考能不能改进空闲内存的分配方案,从而减缓小碎片问题,思考如下几种分配方案:
- 首次适应:上述分配方案即首次适应,含义是:每次分配的时候从头开始寻找合适的空间分配给进程
- 循环首次适应:比如第一次分配的是0-1M的空间,那么下次分配的时候从1M开始向后找,上述两种方案没有太大的本质区别
- 最佳适应:遍历整个内存空间,寻找大小最接近的空间分配。比如内存空间中1-3M, 5-6M,10-15M都是空闲的,现在某进程需要1M的内存空间,那么将5-6M给它,这种方案有时能避免碎片,但有时不行,比如某进程需要0.9M的空间,那么给他1M,另外0.1M就成碎片啦
- 最坏适应:与上述方案相反,还是使用上面的数据,某进程需要1M的空间时,将10-15M的空间给它,即给它大小最不合适的。这种分配方案可以避免此类碎片的生成:需要0.9M,不给它1M,因为如果给它1M,会形成0.1M碎片,所以从10-15M的空间中给它
看完了上述分配方案,其实不难发现,别管使用哪种方案,都无法避免碎片的产生,所以继续研究新的机制。
既然碎片无法避免,那就想办法将碎片集中起来:
- 碎片的紧凑
- 即运行一段时间后会产生碎片,产生碎片后我做一个紧凑操作,将碎片集中起来。
由于目前操作系统并不采用可变分区管理方式,所我们不深究上述机制,不过可以简单想想:紧凑操作不是那么容易,需要移动其他进程的内存空间,可能会带来新的问题。
连续分配存储方案总结
优点:
- 十分简单
- 只需要很简单的硬件支持
缺点:
- 碎片问题难以解决
- 紧凑操作可能带来新问题
连续分配在嵌入式设备中有用武之地,现代操作系统没有采用连续分配方案。
2.分页存储管理
上面我们讲了连续分配方案,也阐明了连续分配方案的缺点,下面我们针对连续分配的缺点继续思考:
- 连续分配最大的缺点在于碎片问题
- 我们使用了分区的思想来缓解了碎片问题
- 我们把思路再回到固定分区管理方案:该方案是给每个进程分配一个分区,如果某个小分区比较小,而我要运行的程序恰恰比较大,那这个小分区就类似于碎片了
- 给每个进程分配一个分区,粒度比较大,因为如果程序比较大,整个分区就浪费掉了
- 那么能不能把粒度做小! 比如:我将内存划分成一小块、一小块,给每个程序分配多个小块,这样粒度变小了,浪费的内存空间也变小了
- 但是即使将粒度做小,也无法避免碎片化问题,再想想碎片产生的根本原因是什么?根本原因在于给每个进程分配的空间都是连续的
- 比如现在有0-1000小块内存,出现这样的场景:A进程得到了1-9块内存,B进程得到了10-15块内存,A执行结束,释放了1-9块,C进程进来得到了1-8块内存,那么离散的第9块内存就被置空了
- 上述场景就是碎片产生的原因,别管用什么空闲内存分配算法都无法避免上述碎片的产生
- 那我们的思路就来了,能不能将离散地分配在各个角落的小块碎片利用起来,或者说直接将小块的分配完全离散化,不就能避免碎片化问题了嘛!
- 至此:分页的思想产生,首先是分成块,上述的块就是页的概念,然后离散管理各个页,此即离散管理的思想。
我们一步一步地思考出了如何克服碎片化问题,重要的思想:将内存划分成一小块、一小块,然后离散管理。
上述思想很重要,以后还会用到,即离散化克服碎片问题(因为碎片就是离散的,干脆完全离散分配,那么碎片问题就解决了)
既然要将内存的分配离散化,那么我们先来直观的看下离散化后的场景:
本来代码是连续分布在内存中的,现在离散化了,每段代码都离散的分布在各个角落。
问题来了:代码的分布都这么散乱,怎么管理呢?
讲解上述问题之前先补充下几个概念:
概念补充
- 页面:逻辑地址空间划分为多个页面,如上图中的一页一页,称为页面
- 页框:物理地址空间划分成多个页框,或物理块,是在物理内存中的表述。
- 我们写程序的时候多用页面,因为操作系统的内存管理将物理地址屏蔽掉了,我们操作的都是逻辑地址或虚拟地址
- 页面和页框大小是一致的
- 进程请求页面数不应该超过系统剩余页框数
- 页面大小:常用4KB,当然如果塞不满4KB的话就浪费掉了(虽然克服了连续分配的碎片化问题,但是分页机制也是有浪费存在的)
先思考一个简单的问题:
- 分页就分页,为什么要引出页面和页框的概念?
- 首先页面是逻辑地址的表述,页框是物理地址的表述!
- 那么问题又来了,为什么要分出逻辑地址和物理地址?只用一个物理地址不行吗?
- 这里我们暂且不深究,原始是因为历史上出现过分段的概念,还有虚拟内存的原因,这里就假设笔者有一定的基础,了解过分段机制,或者看下面分段章节的讲解。
还会引出一个问题:页面是逻辑地址的表述,页框是物理地址的表述,那么两者如何转换呢?两者的对应关系是什么?
如何管理离散页?
现在我们来回答上面提到的问题:
代码的分布那么散乱,怎么管理呢?
其实思想也很简单:图书馆那么多书,也是离散的分布在各个楼层的各个角落,怎么管理的?
- 当然是用数据结构存起来
- 数据结构那么多,用什么数据结构?
- 从简单开始看:数组和链表鸭
再来确定下我们需要的数据结构:
- 我们上面提到的管理本质是什么?
- 是页面和页框的对应关系!
- 我们所说的离散化其实是离散的分配和使用物理页即页框
- 代码的执行还是要顺序执行的,物理内存都离散了怎么保证顺序执行?
- 让逻辑地址对应的页面是连续的就OK了鸭
向上滚动一下,再看下上面的图:
- 假设左边的(01)、(23)、(4)分别对应一个页,右边就是将代码离散的装入物理内存后的情景
- 我们假设将(01)装入了5号页框,(23)装入了3号页框、(4)装入了6号页框,这样就体现了离散装入物理内存了!
- 我们对左边重新编号,(01)对应逻辑页面号0, (23)对应1, (4)对应2:
- 上图的中间部分就是我们的数据结构需要做的事情,管理页面号和页框号之间的映射关系
设计出了这种方案,那么代码怎么执行呢?
- 代码按照逻辑地址执行
- 首先执行到0号页面,查表找到对应页框号是5
- 还有页内偏移的概念,假设0号页面中的call指令的页内偏移是1,那么在5号页框的页内偏移也是1,即能够顺利找到某个页表对应的指令
- 思想就是查表+偏移
这样我们的数据结构就设计出来了,而且已经验证过,程序可以运行,那么我们思考:数据结构就这么简单吗?还需要其他信息吗?
- 比如本页是保存的代码,那么代码应该是只读的,怎么标识?
- 当然还应该有其他信息,比如特权级等
- 我们成上述数据结构为页表
- 引出此问题的目的想说明:页表不仅仅包含上述两项,还有其他内容,我们为了简化问题,后面页仅仅讨论只有上述两项内容的页表。
- 这里虽然将问题简化了,但是不要着急,分页的思想讲述完毕以后,我会讲解一段Linux源码来讲解真实的分页场景
实例练习
该程序页数:100000/1000=100页
逻辑地址1002的物理地址:
- 该逻辑地址对应的逻辑页面号为1
- 则页框号为2
- 页内偏移为2,那么物理地址为2*1000+2=2002
逻辑地址4010的算法类似。
我们将上述的计算过程一般化:
- 假设一页有S字节,程序大小为L字节,该程序有多少页?
- L/S向上取整
- 页表等价于一个映射函数m(lp),lp指逻辑页面号,m(lp)返回逻辑页对应的物理页框号pb
- 如何根据逻辑地址la算物理地址ba?
- 逻辑页号lp=la/S
- 页内偏移=la%S
- 物理块号pb=m(lp) 是一个查表的过程
- pa=m(la/S)*S+la%S
上面提到了逻辑地址,那么逻辑地址长啥样?
- 首先我们来算一下:一页占4K=212
- 那么我们需要用12位才能表示页内偏移
- 假设32位机器,那么逻辑地址32位,其中12位用来表示页内偏移
- 那么剩余的20位用来表示页号:
逻辑地址和物理地址的转换可以用下图表示:
- 页表寄存器记录了页表初始地址
- 页表初始地址+页号 就能找到对应的页框号
- 对应的页框号+偏移就是物理地址
分析上述方案的优缺点
问题1:
我们结合上图来分析:取一个逻辑地址里的操作数的过程:
- 首先从页表寄存器里拿到页表基地址
- 基地址+页号*页表项 大小得到我们需要的页表项地址
- 访问该地址,得到页框号
- 页框号+页内偏移 得到对应的物理地址
- 访问该物理地址得到操作数
即上述方案方案了两次内存,会影响效率。
问题2:
我们先做下简单的运算:
- 规定我们要寻址4GB的内存地址空间
- 一个页框大小为:4KB=212B
- 4GB能划分成多少个页呢? 4GB/4KB=220个
- 那么每个页表项的大小呢(就是我们之前讨论的数据结构)?
- 上面运算得到共有220个页框,那么页框号需要用20个比特位来表示
- 那么每个页表项最起码要有20bit(逻辑页面号当作数组下标,只保存页框号即可)
- 实际上每个页表项占4个字节,即32bit,为何?一是因为需要保存额外的信息,二是因为4字节对齐的考虑。至于为什么要4字节对齐,参考博客
- 现在我们知道了:一个页表项占4个字节,那么一个页框可以容纳多少个页表项呢?
- 4KB/4B=1K=210=1024个页表项
- 这样算下来:4GB有220个页,即我的页表需要有220个页表项
- 一个页框能装1024个页表项,那么装220个页表项需要210个页框。
上面的计算可能优点乱,需要仔细看下,计算的目的是为了明确:
我们的一个页表装在了1024个页框里如下图所示:
这个页表实在是太大了,其需要1024个页框才能装下,即4M的内存空间。
页表太大会带来什么问题?
- 这一个问题涉及到虚拟地址空间相关的知识,可以看我的另一篇博客
- 虚拟地址空间所起到的作用是让每个进程都以为自己运行在0-4G的地址空间里(假设内存是4G)
- 需要将整个页表都复制给每个进程,这会增加创建进程的开销,而且会浪费内存空间。
- 上述问题如果放在64位地址的计算机上,按照相同计算方法,一个进程的页表占用的空间会非常非常大。
- 关键是:一个进程,真的会需要一整个虚拟地址空间去存放吗?
举个例子来说明上述问题:
- 一个需要12MB的进程,底端是4MB的程序正文,后面是4MB数据,顶端是4MB堆栈,在数据顶端上方和堆栈之间是大量没有使用的空闲区
- 注:一个页框保存1024个页表项,能寻址4MB内存
- 既然我们只用到了3个页框的页表,那么能不能只保存相关这三个表的信息,如下图:
3.多级页表
多级页表能减少每个进程需要保存的信息
如上图所示:每个进程中只需要保存一个4KB大小的一级页表,用来记录1024个二级页表中哪个用到,哪个没有用即可,要比保存4MB大小的所有二级页表划算的多。
使用了二级页表的管理机制以后,32位逻辑地址应该如何划分呢?我们需要做下计算:
- 共有1024个二级页表,那么就需要1024个一级页表项来管理
- 一个一级页表项占4字节内存,保存二级页表地址。1024*4B=4KB,一个页框刚好能装得下
- 既然只有一个一级页表,那么逻辑地址中直接保存一级页表的索引号即可,需要用10个bit。
- 查询一级页表后定位到二级页表,然后需要10bit的偏移,用来找二级页表项。
- 一级页表又称外表也称页目录,二级页表又称内表。
- PDE称为页目录项,PTE称为页表项
- 所以32逻辑地址划分成:
二级页表如何将逻辑地址转换成物理地址?
先介绍一个寄存器CR3:
地址转换方式:
Linux0.11版页初始化源码解析+虚拟存储管理+分段存储管理放在下一篇博客,这篇太长了。
如果觉得写的不错,对读者有帮助,可以给笔者点个赞,鼓励一下哦~
本系列博客目录
下一篇:内存管理续
操作系统-课堂笔记-内存管理(南航)相关推荐
- 操作系统-课堂笔记-文件系统(南航)
文章目录 文件系统 引言 文件系统需要做什么工作? 1.关于文件名的限制: 2.文件扩展名有哪些 3.文件有哪些类型? 4.文件是怎么保存的以及文件的属性和操作 文件的实现 如何为文件分配磁盘空间? ...
- 《现代操作系统》笔记-内存管理3
接上 内存管理 页置换算法 1. 随机挑选一个置换 缺点:有可能置换出频繁使用的页 2. 最佳页置换算法 缺陷: 前提是必须要知道接下来哪些页面要使用,完全理想的情况,不可能实现.但可作为衡量算法优劣 ...
- Linux内核笔记--内存管理之用户态进程内存分配
内核版本:linux-2.6.11 Linux在加载一个可执行程序的时候做了种种复杂的工作,内存分配是其中非常重要的一环,作为一个linux程序员必然会想要知道这个过程到底是怎么样的,内核源码会告诉你 ...
- 利用图文和代码深度解析操作系统OS的内存管理实现原理机制和算法
利用图文和代码深度解析操作系统OS的内存管理实现原理机制和算法. 内存作为计算机系统的组成部分,跟开发人员的日常开发活动有着密切的联系,我们平时遇到的Segment Fault.OutOfMemory ...
- 从零手写操作系统之RVOS内存管理模块简单实现-02
从零手写操作系统之RVOS内存管理模块简单实现-02 内存管理分类 内存映射表(Memory Map) Linker Script 链接脚本 语法 基于符号定义获取程序运行时内存分布 基于 Page ...
- 《现代操作系统》第3章读书笔记--内存管理(未完成)
写在前面:本文仅供个人学习使用,如有侵权,请联系删除.文章中所用图片绝大多数来源于<现代操作系统(第4版)>,请读者支持原版. 内存(RAM) 是计算机中一种需要认真管理的重要资源.一个事 ...
- 操作系统-课堂笔记-进程概述(南航)
文章目录 进程概述 1.引言 2.进程的概念 2.1进程的内存抽象 2.2分段保护 2.2.1例一 2.2.2例二 2.2.3小结 2.3进程的状态 版本1 版本2 提升思考(可跳过,涉及虚拟内存) ...
- 【操作系统】考研の内存管理方法(看不懂你来打我~!)
文章目录 1 内存管理概述 1.1 存储层次结构 1.2 指令数据绑定到内存地址 1.3 逻辑地址 2 连续内存管理 2.1 单独分区分配 2.2 固定分区分配 2.3 动态分区分配 2.4 可重定位 ...
- 操作系统原理之内存管理(第四章第二部分)
一.基本分页存储管理方式 1.分⻚存储管理的基本原理: 页:将⼀个进程的逻辑地址空间分成若⼲个⼤⼩相等的⽚ 页框:将物理内存空间分成与⻚⼤⼩相同的若⼲个存储块 分⻚存储:将进程中的若⼲⻚分别装⼊多个可 ...
最新文章
- [ant]通过Android命令自动编译出build.xml文件
- 【对讲机的那点事】节日出游对讲机选择你了解多少?
- AS插件-Android Drawable Importer
- jQuery-给ul添加了li之后,添加的li并没有绑定点击监听怎么办?
- 2018-2019-1 20189210 《LInux内核原理与分析》第六周作业
- 使用Nginx过滤网络爬虫
- 【转】深入理解Windows消息机制
- 设置密码命名是什么linux,orapwd 工具建立密码文件遵守的命名方法
- 信息学奥赛一本通C++语言——1029:计算浮点数相除的余
- PostgresException: 42883: function ifnull(integer, integer) does not exist
- ASP.net的PDF打印(水晶报表)[摘]
- how to switch between python3.5 and python3.6
- mac的终端通过ssh远程连接Linux服务器
- POJ1961 Period
- Arduino UNO驱动DS1307数字实时时钟RTC
- Introduction to Robotics 总结1~6
- 1194_SICP学习笔记_霍夫曼编码树
- PAT A 1034
- stream_kws_cnn
- asp.net房屋出租销售网
热门文章
- 基于SIFT特征的图像配准(附Matlab源代码)
- 【并查集,Bfs】汽车拉力比赛
- Axure RP小技巧:如何利用矩形制作各种形状
- Ubuntu安装openssh遇到依赖问题
- Chrome 远程桌面
- Expandable Input TextView
- 龙族幻想学院计算机中心在哪,龙族幻想社团执事在哪里 社团执事坐标位置介绍...
- mysql数据复制改一个字段_mysql表复制和修改部分字段
- 平板电脑性价比排行,新发布的荣耀平板V6强不强?
- 跨国企业在中国 | 7000多台霍尼韦尔设备助力广州地铁;玩具反斗城中国门店破200家...