内存管理(OS)详解

大家好 我是积极向上的湘锅锅💪💪💪


1、介绍

操作系统的内存管理主要负责内存的分配与回收(malloc 函数:申请内存,free 函数:释放内存),另外地址转换也就是将逻辑地址转换成相应的物理地址等功能也是操作系统内存管理做的事情

总结来说:如果直接把物理地址暴露出来的话会带来严重问题,比如可能对操作系统造成伤害以及给同时运行多个程序造成困难


2、虚拟地址(逻辑地址)与物理地址

首先思考为什么要有虚拟地址空间呢?
假设没用虚拟地址空间,程序直接访问和操作的都是物理内存 ,会产生什么问题?

  1. 用户程序可以访问任意内存,寻址内存的每个字节,这样就很容易(有意或者无意)破坏操作系统,造成操作系统崩溃,这里的设计思想跟用户态和内核态一致。
  2. 想要同时运行多个程序特别困难,比如你想同时运行一个微信和一个 QQ 音乐都不行。为什么呢?举个简单的例子:微信在运行的时候给内存地址 1xxx 赋值后,QQ 音乐也同样给内存地址 1xxx 赋值,那么 QQ 音乐对内存的赋值就会覆盖微信之前所赋的值,这就造成了微信这个程序就会崩溃。

通过虚拟地址访问内存有以下优势:

  • 程序可以使用一系列相邻的虚拟地址来访问物理内存中不相邻的大内存缓冲区
  • 不同进程使用的虚拟地址彼此隔离。一个进程中的代码无法更改正在由另一进程或操作系统使用的物理内存,这就是分段有助于共享和保护的好处
  • 程序可以使用一系列虚拟地址来访问大于可用物理内存的内存缓冲区。当物理内存的供应量变小时,内存管理器会将物理内存页(通常大小为 4 KB)保存到磁盘文件。数据或代码页会根据需要在物理内存与磁盘之间移动,比如4G的虚拟内存,实际上只有1G的物理内存,在操作系统也是可以实现的

3、虚拟内存

每个进程创建加载的时候,会被分配一个大小为4G的连续的虚拟地址空间,虚拟的意思就是,其实这个地址空间时不存在的,仅仅是每个进程“认为”自己拥有4G的内存,而实际上,它用了多少空间,操作系统就在磁盘上划出多少空间给它,等到进程真正运行的时候,需要某些数据并且数据不在物理内存中,才会触发缺页异常,进行数据拷贝

在这里插入图片描述
用户态和内核态:

为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围

处理器通常是用某个控制寄存器中的一个模式位来提供这种观功能的,该寄存器描述了进程当前享有的特权。

  • 当设置了模式位时:进程就运行在内核态中。
    运行在内核态中的进程可以执行指令集中的任何指令,并且可以访问系统中的任何内存位置。
  • 没有设置模式位时:
    进程就运行在用户态。用户模式中的进程不允许执行特权指令。
    比如∶停止处理器、改变模式位、或者发起一个I/O操作。同时也不允许用户态下的进程直接引用地址空间中内核空间的代码和数据。
    任何这样的尝试都会导致致命的保护故障。用户态下的程序必须通过系统调用接口间接的访问内核代码和数据。

为什么需要虚拟内存?

  • 虚拟内存可以结合磁盘和物理内存的优势为进程提供看起来速度足够快并且容量足够大的存储
  • 虚拟内存可以为进程提供独立的内存空间并引入多层的页表结构将虚拟内存翻译成物理内存,进程之间可以共
    享物理内存减少开销,也能简化程序的链接、装载以及内存分配过程;
  • 虚拟内存可以控制进程对物理内存的访问,隔离不同进程的访问权限,提高系统的安全性。

那操作系统该如何管理虚拟地址与物理地址的关系?

go on reading


4、段式存储管理

段式管理把主存分为一段段的,段是有实际意义的,每个段定义了一组逻辑信息,例如,有主程序段 MAIN、子程序段 X、数据段 D 及栈段 S 等。 段式管理通过段表对应逻辑地址和物理地址

程序中的地址是一个偏移量,在载入的时候,找到一个逻辑地址的基址(找到一个空间段),加上该偏移量,得到一个逻辑地址,再映射到物理地址,这个过程就是载入重定位(第一次载入)

运行时重定位:操作系统中有进程的切换,之前载入的程序可能阻塞(被交换到硬盘)一段时间之后重新载入内存,之前的重定位方法就不可行了,因此程序还是先载入偏移量,在某条指令运行的时候再加上基址,进行重定位

程序的分段:

  • 一般可以按照代码段,数据段隔离开来,代码段可以设置成只读的,数据段可以动态增长,数据段的修改不会影响到代码段
  • 将一个程序分段放入内存,数据段的增长不会挤压代码段的空间
  • 分段之后,寻址必须要引入段号了,每一个段都有基址存放在段表中
    1. LDT表,进程的段表
    2. GDT表,OS的段表

分段主要是为了使程序和数据可以被划分为逻辑上独立的地址空间并且有助于共享和保护

在内存分段之后,整个程序就流程如下:

  • 程序自身分段
  • 在内存找到一个空闲的分区,通过磁盘读写将程序的段放入内存

因为程序的每一个段大小都是不固定的,所以需要寻找一个合适的分区,有三种方案

  • 最佳适配:割最小的空间,碎片越来越小
  • 最差适配:割最大的空间,碎片比较均匀
  • 首先适配:不扫描所有,直接割第一个适配的

对比:

  1. 可以看出前俩种需要O(n)也就是找出所有分区里面的最大最小

  2. 而首先适配最差才是O(n),最好是O(1),但是碎片化就比较严重

问题:

  1. 会产生内存碎片,总内存够,但是没用一块连续的分区可以放入了,因为内存紧凑花费时间长,对用户而言那就是卡死
  2. 因为会涉及到多次CPU寻址,对CPU也有负担

CPU寻址:CPU 需要将虚拟地址翻译成物理地址,这样才能访问到真实的物理内存。 实际上完成虚拟地址转换为物理地址转换的硬件是 CPU 中含有一个被称为 内存管理单元(Memory Management Unit, MMU) 的硬件


5、页式存储管理

如何解决上述问题?

OS引入分页,将固定大小页面做为分配的单位,就不会有碎片了(页内碎片很小)

把主存分为大小相等且固定的一页一页的形式,页较小,相比于块式管理的划分粒度更小,提高了内存利用率,减少了碎片。页式管理通过页表对应逻辑地址和物理地址

这个时候页面的大小就要适中了,过大难以分配,过小内存碎片过大,页面大小通常是512B-8K

对于32位寻址的虚拟地址,如果页面大小为4KB,则虚拟页面数最多可以达到2的20次方,即1048576个虚拟页面,那么页表的记录条数就为1048576条,所以引入多级页表

在这里插入图片描述

引入多级页表的主要目的是为了避免把全部页表一直放在内存中占用过多空间,特别是那些根本就不需要的页表就不需要保留在内存中。多级页表属于时间换空间的典型场景,具体实现思想参考数据库的索引

为何可以节省空间?

  • 时间局部性:某条指令可能由于循环反复执行
  • 空间局部性:一个地址被访问,该地址附件的地址可能也被访问,比如数组的地址空间是连续的
  • 一级页表来映射二级页表,所以每个映射就是一个二级页表的大小,所以二级页表大部分可以不存在,也可以不再主存中

如何提升虚拟地址到物理地址的转换速度?
引入快表,我们可以把快表理解为一种特殊的高速缓冲存储器(Cache),其中的内容是页表的一部分或者全部内容。作为页表的 Cache,它的作用与页表相似,但是提高了访问速率。由于采用页表做地址转换,读写内存数据时 CPU 要访问两次主存。有了快表,有时只要访问一次高速缓冲存储器,一次主存,这样可加速查找并提高指令执行速度,就好比Redis缓存模式,查询缓存总比磁盘快上不少

使用快表之后的地址转换流程是这样的:

  • 根据虚拟地址中的页号查快表;
  • 如果该页在快表中,直接从快表中读取相应的物理地址;
  • 如果该页不在快表中,就访问内存中的页表,再从页表中得到物理地址,同时将页表中的该映射表项添加到快表中;
  • 当快表填满后,又要登记新页时,就按照一定的淘汰策略淘汰掉快表中的一个页。

总结:为了提高内存的空间性能,提出了多级页表的概念;但是提到空间性能是以浪费时间性能为基础的,因此为了补充损失的时间性能,提出了快表(即 TLB)的概念。 不论是快表还是多级页表实际上都利用到了程序的局部性原理,局部性原理在后面的虚拟内存这部分会介绍到


7、分页机制和分段机制的共同点和区别

共同点 :
分页机制和分段机制都是为了提高内存利用率,减少内存碎片。
页和段都是离散存储的,所以两者都是离散分配内存的方式。但是,每个页和段中的内存是连续的。
区别 :
页的大小是固定的,由操作系统决定;而段的大小不固定,取决于我们当前运行的程序。
分页仅仅是为了满足操作系统内存管理的需求,而段是逻辑信息的单位,在程序中可以体现为代码段,数据段,能够更好满足用户的需要。

在这里插入图片描述


8、段页式存储管理

分页和分段管理方式各有其优缺点,分页系统能有效提高内存的利用率,而分段则能更好地满足用户的需要,因此可以将两者结合成一种新的存储管理方式系统称为“段页式存储管理”

结合分段和分页思想

  1. 先将逻辑空间按照段式管理分成若干段。
  2. 再把段内空间按照页式管理等分成若干页。

在这里插入图片描述
段页式系统中,获得一条指令或数据需要访问三次内存

  1. 访问段表,获得页表始址
  2. 访问页表,获得物理块号
  3. 访问真正的数据

9、页面置换算法

地址映射过程中,若在页面中发现所要访问的页面不在内存中,则发生缺页中断 。

当发生缺页中断时,如果当前内存中并没有空闲的页面,操作系统就必须在内存选择一个页面将其移出内存,以便为即将调入的页面让出空间。用来选择淘汰哪一页的规则叫做页面置换算法,我们可以把页面置换算法看成是淘汰页面的规则

  • OPT 页面置换算法(最佳页面置换算法) :最佳(Optimal, OPT)置换算法所选择的被淘汰页面将是以后永不使用的,或者是在最长时间内不再被访问的页面,这样可以保证获得最低的缺页率。但由于人们目前无法预知进程在内存下的若千页面中哪个是未来最长时间内不再被访问的,因而该算法无法实现。一般作为衡量其他置换算法的方法。
  • FIFO(First In First Out) 页面置换算法(先进先出页面置换算法) : 总是淘汰最先进入内存的页面,即选择在内存中驻留时间最久的页面进行淘汰。
  • LRU (Least Recently Used)页面置换算法(最近最久未使用页面置换算法) :LRU 算法赋予每个页面一个访问字段,用来记录一个页面自上次被访问以来所经历的时间 T,当须淘汰一个页面时,选择现有页面中其 T 值最大的,即最近最久未使用的页面予以淘汰。

这篇更加详细 页面置换算法


参考:

JavaGuide
如何理解虚拟内存

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

owensweat

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值