​测试阻碍交付,如何破解这一难题?>>>想和你聊聊操作系统的内存管理_物理地址


大家好,这里是​公众号:java小杰要加油​,这周来分享一个操作系统的相关知识——内存管理



  • 话不多说,直接开车



物理地址 VS 虚拟地址


  • 物理地址​:逻辑上,我们可以把物理内存看成一个大数组,其中每个字节都可以通过与之对应的地址进行访问,这个地址就叫做​物理地址
  • 虚拟地址​ :应用程序在运行时使用的地址

CPU翻译虚拟地址的过程大概如图所示


想和你聊聊操作系统的内存管理_物理地址_02

他们的包含关系如下:​cpu包含MMU,MMU包含TLB

  • CPU


TLB(转址旁路缓存 Translation Lookaside Buffer)​:加速地址翻译的过程 ​MMU(内存管理单元 Memory Management Unit):​ 负责虚拟地址到物理地址的转换

平常加载程序的顺序是


  1. 操作系统把程序从 ​磁盘​加载到 ​内存​中(程序一开始是在磁盘中存放的)
  2. CPU去执行程序的第一条指令但是这个 ​指令现在在物理内存​中
  3. cpu取指令取的是该指令的 ​虚拟地址​,由 ​MMU​翻译为 ​物理地址
  4. 这个读物理地址的请求将通过 ​总线​,传送到相应的 ​物理内存​中,然后 ​物理内存​把该指令发送给 ​CPU



分段

MMU​将​虚拟地址​翻译为​物理地址​主要有​​种机制 :​分段​和​分页




分段机制


  • 操作系统以“段”(一段连续的物理内存)的形式管理/分配物理内存
  • 应用程序的虚拟地址空间由若干个大小不同的段组成:代码段、数据段等等
  • 当 ​CPU访问虚拟地址​中的 ​某一个段​的时候, ​MMU​会通过查询 ​段表​来得到该段对应的 ​物理地址


想和你聊聊操作系统的内存管理_面试_03

虚拟地址:


  • 段号:​ 标志着该虚拟地址属于整个虚拟地址空间中的哪一段
  • 段内地址(段内偏移):​ 相对于该段起始地址的偏移量


当 cpu 读取指令时,发现指令的地址是​虚拟地址​,那么CPU中的​MMU​ 首先判断这个​段号​是否合法,如果合法, 则通过 ​段表基址寄存器​  找到​段表​的位置,通过虚拟地址中的​段号​,找到该段的​起始地址​,再加上​段内地址​(段内偏移),就可以得到​最终的物理地址



  • 在分段机制下, ​虚拟内存​和 ​物理内存​都划分成了 ​不同的段



分段缺点


  • 在虚拟地址空间中,相邻的段所对应的物理内存空间可以不相邻,操作系统能够实现物理内存资源的离散分配,但是这种段式分配方式容易导致在物理内存上出现 ​外部碎片


图中装载不进来的就是​外部碎片想和你聊聊操作系统的内存管理_虚拟地址_04



分页机制

  • 基本思想:

  • 将应用程序的 ​虚拟地址​空间划分为 ​连续的​、 ​等长的​虚拟页(4K)
  • 物理地址​也是划分为 ​连续的​、 ​等长的​的物理页
  • 物理页​和 ​虚拟页​页长 ​固定且相等

之所以这样构造是因为会使​操作系统​很方便的为​每个应用程序​构造​页表​,即虚拟页和物理页​映射关系表


  • 在分页机制下,应用程序虚拟地址空间中的任意虚拟页可以被映射到物理内存中的任意物理页上,可以避免​外部碎片​的问题
  • 分页机制下的​虚拟地址​也由两部分组成:​虚拟页号:​ ​页内偏移量:


想和你聊聊操作系统的内存管理_java_05

翻译的具体流程就是:


  1. MMU​首先解析 ​虚拟地址​中的 ​虚拟页号​,检查这个虚拟页号 ​是否合法​,通过这个 ​虚拟页号​取该应用程序的 ​虚拟页表​中找到对应条目(页表起始地址放在 ​页表基地址寄存器​)
  2. 然后取出该条目中的 ​物理页号
  3. 最后用该 ​物理页号​对应的 ​物理起始地址​加上 ​虚拟地址​中的 ​页内偏移​得到 ​最终的物理地址




TLB

首先要说一下​局部性原理


  • 时间局部性:​ 如果执行了程序中的某条指令,那么不久后这条指令很有可能再次执行,如果某个数据被访问过,不久后该数据很可能再次被访问(因为程序中存在大量的循环)
  • 空间局部性:​ 一旦程序访问了某个存储单元,在不久之后,其附近的存储单元也很有可能会被访问(因为很多数据在内存中都是连续存放的)


所以,能不能弄一个缓存,缓存这些有可能会被经常被访问的数据呢,从而减少访问页表的次数呢?

为了减少地址翻译的访问次数,MMU引入​TLB​(转址旁路缓存 Translation Lookaside Buffer)


  • TLB 硬件采用分层架构, ​分为L1、L2两层​。

  • LI​又分为数据TLB和指令TLB,分别缓存数据和指令的地址翻译
  • L2​不区分数据和指令


  • TLB ​缓存​了虚拟页号和物理页号的映射关系,类似map,key是虚拟页号,value是物理页号。

  • 如果在TLB中找到则称为 ​TLB命中
  • 没有找到则称之为 ​TLB未命中


想和你聊聊操作系统的内存管理_物理地址_06

有了TLB之后,查询就变成了


  • 1. ​  MMU​ 首先解析 ​虚拟地址​ 中的 ​虚拟页号​ ,检查这个虚拟页号 ​是否合法​ ,如果合法

    • 查TLB,如果命中则 直接取出物理初始地址,再加上页内偏移量得到最终物理地址,否则继续查询页表
    • 如果页表中存在物理初始地址,则将此物理初始地址缓存到TLB中 通过这个 ​虚拟页号​取该应用程序的 ​虚拟页表​中找到对应条目(页表起始地址放在 ​页表基地址寄存器​)



  1. 然后取出该条目中的 ​物理页号
  2. 最后用该 ​物理页号​对应的 ​物理起始地址​加上 ​虚拟地址​中的 ​页内偏移​得到 ​最终的物理地址





多级页表


  • 如果页表太大时怎么办,页表必须连续存放,会占用很多内存,所以就把一个大表拆成很多小表


拆分后的访问顺序如图所示


想和你聊聊操作系统的内存管理_面试_07


  • 根据一级页号查找到物理页号,这个物理页号里面装的是二级页表的地址,找到此地址后,在根据二级页号 找到物理地址,此物理地址在加上页内偏移量则为最终的物理地址



换页与缺页异常



换页

虚拟内存中的​换页​:当物理内存容量不够的时候,操作系统应当把若干物理页的内容写到磁盘这种大容量的地方,然后​回收物理页​并继续使用


举例:有个应用程序A,A的虚拟页K对应物理页V,这个时候,操作系统想回收物理页V,要怎么做呢?


  • 操作系统把V写到磁盘上
  • 并且在A的页表中除去虚拟页K和物理页V的映射,同时记录物理页V被换到磁盘上的对应的位置


以上这两部被称为物理页V的​换出



缺页异常

缺页异常是换页机制能够工作的前提,当应用程序访问​已经分配但是未映射至物理内存​的虚拟页时,就会触发缺页异常



  • 如何解决:通过 ​换入

    • c pu会运行操作系统预先设置的缺页异常处理函数,该函数会找到一个空闲的物理页,
    • 将以前写 入到磁盘 上的内容重新加载到该空闲的物理页
    • 然后将虚拟地址和此物理地址映射起来


处理完这一切后,cpu回到发生缺页异常的地方继续运行


想和你聊聊操作系统的内存管理_java_08


段页式内存管理

  • 分段管理

  • 优点:​ 很方便的按照逻辑模块实现信息的共享和保护
  • 缺点:​ 容易产生外部碎片


  • 分页管理

  • 优点​ 内存空间利用率高,不会产生外部碎片,只会有少量页内碎片
  • 缺点: 不方便按照逻辑模块实现信息的共享和保护

段页式内存管理



将地址空间按照程序自身的逻辑关系分为若干层,将各段分为大小相等的页面 将物理内存与虚拟内存划分为大小相等的一个个的内存块,系统以块为单位为进程分配内存 逻辑地址/虚拟地址(段号,页号,页内偏移量) 想和你聊聊操作系统的内存管理_java_09


虚拟地址翻译为物理地址的步骤变为


  • 根据逻辑地址取出其中的段号,判断这个段号是否正常
  • 如果正常,则找到该段号对应的页表初始地址
  • 根据页号是否正常,若正常则根据页号找到物理初始地址,在加上页内偏移量则找到真正的物理地址


想和你聊聊操作系统的内存管理_mysql_10






微信公众号 - java宝典(java_bible)。