专栏首页木可大大漫谈虚拟内存
原创

漫谈虚拟内存

虚拟内存是什么?它是对主存和I/O设备的抽象,这一点在漫谈进程和线程中已经提及过,也就是说,虚拟内存是将内存看做硬盘的高速缓存,内存中只保存程序的活动区域,根据需要在硬盘和内存之间传输数据;同时,虚拟内存为每个进程提供一个一致的地址空间,比如说32位计算机,每个进程的地址范围是0,4G。此外,虚拟内存保护每个进程的地址空间不被其他进程破坏,那么,通过何种方法使得每个进程的地址空间是独立的?看完本文,这个问题你自然就知道答案了。

内存管理的要求

  • 针对批处理系统,程序顺序执行,程序依次装入内存运行,一个新装入的程序会完全覆盖老的程序。
  • 针对分时系统,多个程序并发执行,要把尽可能多的程序装入内存,但是物理内存的空间是有限的,经常需要将程序换入换出,这样一来,系统性能就变差了;程序在内存中要区分开。
    内存分配

如上图,程序1、程序2、程序3装入到内存,而程序2运行完成被换出,内存空闲出20k,然后进来程序4,大小为25K,此时,只有两处空闲块,10K和20K,没有一处是符合条件的,应该怎么办?一个明显的办法就是将两块空闲区域进行合并,形成一个大小为30K的空闲块满足程序4。

注意:此时描述的内存的物理地址,物理内存使用的是连续地址,下面详细介绍物理地址。

物理地址

使用物理地址的缺点:当多个程序同时对某个地址进行操作时就会引起冲突,如下图所示,程序1和程序2中都有指令movl eax,(100)。针对这个问题怎么解决呢?

在装载程序的时候,修改指令的地址。例如程序2中的(100)+1000,其中1000代表这个程序的开始地址,而程序1中的(100)+0。这样做是很困难的,因为需要我们理解所有的指令。既然这个方法不好,那么有没有其他方法呢?答案就是逻辑地址

程序1、2地址冲突

逻辑地址

CPU有一个内存管理单元(MMU),它有一个基址寄存器,它保存着每个程序的开始地址,比如说程序1的基址寄存器是1000,偏移量是200,转换成物理地址是1200。

逻辑地址

分页

假设一个程序很大,需要占据所有内存,而内存管理的一个要求就是把尽可能多的程序装入内存,两者相互矛盾。应对方法就是分页,就是说每个程序开始运行时只会加载部分数据到内存中操作系统会为每个进程维护一个页表,页表是维护虚拟页和物理页的映射关系,当页表中的虚拟页对应的物理页是空白时,操作系统会发生缺页中断。分页的理论依据是局部性原理(空间局部性+时间局部性),也正是这个原因,程序在大部分时间内不需要进行页面置换。如果发生缺页中断,缺页中断处理程序读取磁盘,选择一个空闲物理页面,修改页表,重新执行程序。

  • 注意事项
  • 每个进程都要有一个页表,进程PCB有指向页表的指针
  • 页表访问要非常快(硬件缓存来拯救:转换缓冲区--TLB)
  • 页表可能非常大(2^32 的内存空间,每个页大小2^12 ,页表中需要2^20个页表条目,假设每个条目4Byte,需要4M空间来存放表,而且每个进程都需要4M,这是非常占用空间的。可以采用多级页表,反向页表等技术来解决)。
分页
  • 分页具体流程 以CPU执行MOV (0x560) EAX为例,CPU内部会将逻辑地址进行拆分成页号和偏移量,然后将逻辑地址转换成物理地址。
    分页流程

页面置换算法

  • 内存是有限的,不可能把所有的页面都装进来,缺页时需要进行页面置换。
  • 页面置换背后是个通用的问题(Web服务器的缓存、Redis、Memcached的缓存等等)。FIFO(先进先出)先进先出算法思想很简单,当内存满了,优先置换出最先进入内存的页面。但是它存在一个问题:经常被访问的数据有可能被换入换出,下面我就举个简单的栗子。假设只有3个物理页面,逻辑页面的访问次序是: 7 0 1 2 0 3 0 4
FIFO

LRU(最近最少使用)

LRU算法就是所有页用栈组成,当栈满了,且新增加元素没有命中,则将栈底元素淘汰,新增元素放到栈顶;当栈满了,且新增元素命中,则只需要将新增元素移动到栈顶位置即可。假设只有3个物理页面,逻辑页面的访问次序是: 7 0 1 2 0 3 0 4

LRU

Clock算法

Clock算法是LRU算法的近似实现,它为每个页加一个引用位,默认值为0,无论读还是写,都置为1,它把所有的页组成一个循环队列,选择淘汰页的时候,扫描引用位,如果是1则改成0(相当于再给该页面一次存活的机会),并扫描下一个;如果该引用位是0,则淘汰该页,换入新的页面。假设只有3个物理页面,逻辑页面的访问次序是: 3 4 2 6

Clock

分段

上述介绍的页表机制是面向机器的,而为了程序员更好的理解程序,我们的先辈们又提出了分段概念,就是将程序划分为若干段部分,每一段都有独立的功能,例如:代码段,数据段,栈,堆等。通过分段技术,我们把内存空间分成一个个可以自治的段,而且把内存从一维空间变成了一个二维空间。

段表结构

段页结合

段页结合流程:首先根据段表信息,将逻辑地址转换成另一个逻辑地址,在转换的过程中会判断偏移量是否超过指定长度,如果没有超过,则,则根据页表将逻辑地址转换成物理地址。

段页结合

虚拟内存具体实现

这里介绍Linux中的虚拟内存的具体实现,如下图,task_struct结构体是进程描述符,属于进程管理(PCB),其中,mm(memory manage)表示内存管理,它指向mm_struct结构体,它描述linux下进程的虚拟地址空间,它又包含两个重要字段:pgd、mmap,其中,pgd指向第一级页表的基址,而mmap指向一个vm_area_struct(区域结构)的链表,vm_start,vm_end分别表示数据的起始地址,vm_prot描述的是这个区域包含的所有页的读写许可权限;vm_flags描述这个区域是和别的进行共享的,还是该进程私有的

Linux是如何组织虚拟存储器

Linux缺页中断:MMU(内存管理单元)试图翻译一个虚拟地址A,当这个虚拟地址对应的物理地址不在内存中是,触发一个缺页中断。虚拟地址A是合法的吗?地址A在某个区域地址内吗(vm_start,vm_end),如果不存在,segement fault!段错误。如果存在,则接着判断进程是否可以读,写,执行这个区域内页面的权限?如果没有权限,触发保护异常。经过上诉两项判断,如果都是正常的,最后才开始真正的缺页处理,从硬盘装载数据,修改页表。


image

欢迎关注微信公众号:木可大大,所有文章都将同步在公众号上。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 编写优雅代码的最佳实践

    Robert Martin曾说过"在代码阅读中说脏话的频率是衡量代码质量额唯一标准"。同时,代码的写法应当使别人理解它所需的时间最小化,也就是说我们写的代码是给...

    木可大大
  • 面向对象设计必须遵循的几条原则

    我们应该采用何种方法去应对需求变化呢?首先,在方法论层面我们应该采用敏捷开发;其次,在代码层面,使用OOD(Object-Oriented Design,面向对...

    木可大大
  • 编写优雅代码的最佳实践

    Robert Martin曾说过"在代码阅读中说脏话的频率是衡量代码质量额唯一标准"。同时,代码的写法应当使别人理解它所需的时间最小化,也就是说我们写的代码是给...

    木可大大
  • 分页存储管理的基本原理

    在分页存储管理中,一个程序的逻辑地址空间被划分成若干个大小相等的区域,每个区域称为页或页面,并且程序地址空间中所有的页从 0 开始顺序编号。相应地,内存物理地址...

    一滴水的眼泪
  • opoa介绍

    一 定义       One Page, One Application(后面缩写为OPOA,或者1P1A), 含义很简单:一个页面就是一个应用。不再使用ifr...

    hbbliyong
  • iOS内存不够怎么办?-底层原理

    https://juejin.im/post/5ccd6bb3e51d453a5604c42e”

    iOSSir
  • 保持简短和重点来优化语音搜索

    黄伟SEO
  • 不用锤子手机也能 Big Bang!识字、分词,就用这款小程序

    前不久,我在《懒得打字?这两款文字识别小程序,解放你的双手》一文中,推荐了两款「智能识别图文」小程序。

    知晓君
  • python跟H3C 5024E交换机交

    # coding: utf8 import re, sys import pexpect # enable/disable debug mode DEBUG ...

    py3study
  • 小程序开发之路-注册并新建一个小程序

    只要填写相关的信息就可以了。 注册结束以后呢,我们登陆就可以了,登陆的话也是一样的链接,直接可以立即登陆就可以了。登陆进去以后是这样的:

    何处锦绣不灰堆

扫码关注云+社区

领取腾讯云代金券