操作系统之内存

内存分配

所有文章和源码最新更新在我的GitHub,欢迎点个Star~ 传送门

题外话

学习操作系统的念头来源于几次面试中的自闭经历,该好好补基础了

前言

技术的进步通常都是以需求驱动的,早期计算机运行的是单进程,所以直接操作内存,程序占用的内存不要超过主存就OK,但是随着需求和硬件的进步,多进程分时操作系统的到来需要操作系统对内存管理的革新。

内存分配方式

操作系统内存内存分配的方式大体上可以分为两种,连续内存分配和非连续内存分配,现代操作系统主要使用的是以非连续内存分配方式为主,那么连续内存分配就简单介绍带过~

连续内存分配

连续内存分配,顾名思义,分配的内存空间是连续的。

连续内存分配容易产生内存碎片,内存碎片分为:

外部碎片:分配单元之间的未使用的内存

内部碎片:分配单元内未使用的内存

内存碎片有什么危害呢?

零散的小内存碎片不利于程序申请新的空间,从而导致无法有效利用内存,内存的利用率低。

连续内存分配直接操作物理内存

程序直接操作物理内存对程序来说是非常不安全的,这不利于程序之间的隔离性,如果恶意程序恶意访问了其他程序的内存空间,并对其进行恶意修改,都会造成出错甚至崩溃。

程序运行的内存地址不确定

程序每次需要运行时,都需要在内存中寻找一块空闲的区域,那么问题在于空闲的区域是无法确定的,这就会带来一些重定位问题,十分的麻烦。

常见的分配方法有:

  1. 首次适配法:从内存中寻找第一个可用的空闲块进行分配。如下图所示,为分配400b空间,使用首次适配法会选择1k bytes大小的空闲块进行分配,分配完成后原先1K bytes的空闲块变成了600b bytes的空闲块。
    在这里插入图片描述

  2. 最优适配法:从内存的空闲块中选择与预分配内存大小最接近的空闲块进行分配。如下图所示,最接近400b的空闲块为500b的那块,那么就选择它进行分配。
    在这里插入图片描述

  3. 最坏适配法:与最优适配法相反,它选择的是与预分配内存大小差距最大的空闲块进行分配。
    在这里插入图片描述

为了解决内存碎片带来的一系列问题,常见的有两种解决方法:

  1. 压缩式碎片整理:如下图所示,将P2,P3,P4向上移动,使他们的内存地址连续,这样就将原先分散的内存碎片合成了一块相对大的内存空间。
    在这里插入图片描述
  2. 交换式碎片整理:如下图所示,主存中正在运行程序P1,P2,P3,P4,此时活跃的程序P3 需要申请更多的空间,而内存已经被用满了,或者说碎片太小无法分配,此时可以将不活跃的P4暂时移动到磁盘存储,腾出部分空间。
    在这里插入图片描述

非连续内存分配

为什么要使用非连续内存分配?

对应的就是解决上述连续内存分配的一系列问题。

  • 程序的地址空间物理上可以是非连续的(下文会详细解释)
  • 减少内存碎片的产生,提高内存利用率
  • 允许共享内存
  • 有利于程序之间的隔离

常见的非连续内存分配有两种方式,分段和分页,两者都是需要硬件支持的解决方案。

分段:分段是在分页之前一直使用的一种方式,将用户程序的地址空间分成了一个或者若干个大小不能的段,存储分配时,以段为单位,段内连续的存储空间是逻辑上的连续,映射在主存上时可以是非连续的,也就是说程序只需要关心逻辑地址,那么它的内存地址就被固定了。CPU在寻址时是通过段表来进行映射的。
在这里插入图片描述

分段设计很好 的解决了程序内存地址不固定和直接操作内存的问题,但是仍然无法解决内存碎片的问题,于是有了分页。

分页:

分页是将物理内存分成若干个大小固定的帧(Frame),在Linux中通常一个页是4KB(帧和页是一个概念,只不过主存中用帧,页表中用页区分),页(Page)的大小也很重要,如果一个页1MB,而程序只存储了100KB,那么剩下的900KB就成了内碎片。

进程中的虚拟地址空间中的页和物理内存的粒度相同,在每个进程中都有一个页表存放进程内存的映射关系,CPU寻址时只需要根据逻辑地址从页表中获得对应物理地址的帧号(Frame-Number)加上偏移量(offset)即可获得物理地址。如下图所示就是一个简陋的页表映射主存的示例图。
在这里插入图片描述
分页机制把主存分割成了细粒度较高的小内存块很好的解决了分段中的内存碎片问题,但是我们仔细思考:

  1. CPU每次寻址需要访问两次内存。

    一次访问页表获得映射地址,一次根据具体地址访问数据,而CPU寻址又是非常频繁的,主存的读写速度相对于CPU的处理速度是十分缓慢的,这对CPU的执行非常的不利。

    解决:在CPU的MMU(Memory Management Unit)内存管理单元中有TLB(Transaction Look-aside Buffer)这是一个缓冲区,它使用的是相关存储器,具有快速查询的特性,同时它的空间也比较小,以Key-Value形式缓存常用的页表项,这样一来CPU在寻址时会先到TLB中寻找,如果命中则直接获得帧号+偏移量即物理地址,如果TLB中不存在再去访问页表,同是再将其缓存入TLB中

  2. 页表占用内存大。

    以32位系统为例,CPU的寻址范围(通俗的说就是能用多大的内存)为2的32次方,也就是4GB内存,假设分页时每个页映射4KB内存,每条的页表项占据4B大小,一个程序中的页表需要占据多大的内存呢?

    4GB / 4KB = 1MB // 共有1MB个帧(页)

    1M * 4B = 4M // 一个页表占用4M的空间

    通常系统中运行上百个进程,页表就要占据了大部分的内存空间,显然是十分浪费资源的,如果是64位系统页表占据的空间会更加庞大,那么应该如何解决呢?

    解决:多级页表,多级页表的概念有点像MySQL中的B+Tree索引树的存储结构,以两级页表为例,如下图所示,一级页表的Key存放的是一段地址的范围,value中存放的是二级页表的起始地址。二级页表的value则存放于主存对应的信息。

    在这里插入图片描述

    CPU寻址时拥有P1和P2 的偏移量和一级页表的起始地址,先用P1的的起始地址加偏移量获得对应的页表项获得二级页表的起始地址,再用二级页表起始地址+P2获得二级页表的Value即可获得主存中的地址。

    那么它和节约存储空间又有什么关系呢?

    多级页表它必须存储的是一级页表,和一级页表下所使用的到的子页表项,并不需要将所有的多级页表全部存在主存中。还是使用上述的条件进行举例:

    一级页表中每个页映射4MB,二级页表映射4KB,那么需要(4GB / 4MB = 1KB)个一级页表项,一级页表占用

    (1KB * 4B = 4KB),而二级页表项则是按需存储,相比单级页表的4MB要节约得多了。

    结合实际:

    讲了那么多理论基础,结合实际编程捋一遍

    创建进程fork()、程序载入execve()、映射文件mmap()、动态内存分配malloc()等进程相关操作都需要分配内存给进程,此时进程申请分配到的并不是实际内存,而是虚拟地址空间的一段区间的使用权,也印证了进程只能够操作虚拟地址空间,当CPU真正的访问进程的这部分空间时,会产生一个缺页异常,告诉内核真正的去为进程分配物理页,MMU将虚拟地址映射到分配的物理内存上(还有个情况是页被置换到磁盘中存储,那就不用建立映射了,而是通过具体的算法进行置换)。

    说点心得:

    这种内存分配机制很像懒加载,直到必须进行的时候才会加载,从而提高系统的运行效率,很充分的利用了程序的局部性原理,不得不说操作系统真的是编程的教科书。

虚拟内存

随着软件的发展,占用的内存越来越大,有什么办法能够低成本又有效的对内存进行扩展,虚拟内存正是因此而来。

虚拟内存实现的目标:

科普程序局部性:

时间局部性:一条指令的一次执行和下一次执行,一条数据的访问和下一次访问都集中在一个较短的时期内。

空间局部性:当前指令和临近的几条指令,当前访问的数据和临近的几个数据都集中在一个较小的区域内。

根据程序的局部性原理,并不把程序的所有内容都存储在内存中,而是按需加载,并且扩展一部分的磁盘存储空间作为主存的置换区域,可以根据需求将数据在主存和磁盘中进行互换,以上的步骤均为操作系统完成,无需程序员关注。

基于段页式的虚存管理:

  1. 当一个用户程序调入内存运行时,只装入使用到的部分页面。

  2. 在运行过程中,如果产生缺页异常再通知内核进行装入。

    缺页中断的处理过程:

    内存有空闲空间,直接将需要的页装入到物理内存对应的帧中,维护页表映射。

    内存中无空闲空间:

    1. 采用某种页面置换算法,选择被替换的物理页帧f,它对应的逻辑页为q,如果该页数据被修改过则需要先写回外存。
    2. 将q的数据存入磁盘中,再将需要访问的页x装入物理内存q中
    3. 重新维护页表映射

几种常见的置换算法:

最优页面置换算法,先进先出算法(FIFO),最久未使用算法(LRU),时钟页面置换算法,最不常用算法(LFU)等。这些算法在这不做详细介绍,需要了解的可以看:http://c.biancheng.net/view/1277.html

参考:

首先推荐一个清华大学的操作系统公开课讲的非常不错:https://www.bilibili.com/video/av6538245/?p=14。本文截图多出来源于此。

https://www.cnblogs.com/shenckicc/p/6884921.html

https://www.cnblogs.com/thrillerz/p/6031561.html

统公开课讲的非常不错:https://www.bilibili.com/video/av6538245/?p=14。本文截图多出来源于此。

https://www.cnblogs.com/shenckicc/p/6884921.html

https://www.cnblogs.com/thrillerz/p/6031561.html

https://www.cnblogs.com/ralap7/p/9184773.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值