MMU工作原理,理解虚拟内存的奥秘

  内存是各个操作系统的核心,学习好内存管理不但有助于对各种编程语言更好的理解,也是学习信息安全的必备知识。例如:缓冲区溢出攻击(buffer overflow attacks) 和 旁道攻击(side-channel attacks) 都需要对内存知识很牢固的掌握。

MMU(Memory Manage Unit) 存储管理器单元

最初的内存管理可以分为两个派别:一派是依赖在主存(memory)和磁盘(disk)来回移动执行内容的进程(processes), 主要用的技术是swaping和paging;一派是通过优化算法. 优化算法比较简单,这里我们从简单的方法入手,逐步深入学习swaping和paging。

1: “猿猴时代”的内存

最为简单的内存管理机制不过是每次在内存中只运行一个客户端程序,注意,这里强调是客户端,因为内核程序(含设备驱动程序)是常驻内存的。内核程序可能位于RAM (random access memory :a)或者位于ROM(read only memory :b)。或者将内核程序一分为二,设备驱动程序位于ROM中,其他内核程序位于RAM中 :c。见图一:

图一:内核程序的三种分部方式
用户执行程序,操作系统从硬盘上将程序拷贝至内存,执行完毕,操作系统等待下一个用户程序,新的用户程序将会覆盖掉上一个程序占用的内存。这种机制最大的弊端是:如果用户程序等待I/O输入的话,其他用户程序不能执行。这样就浪费了CPU的资源
2:“直立行走”
“猿猴时代的内存”明显解决不了多个用户同时使用内存的情况 即:一个程序等待I/O响应时,其他程序依然可以使用CPU。解决这个问题最简单的方式莫过于 将内存分成几个partitions。当来一个任务时,可以合理的选择partition来执行,其他partitions可以被其他用户程序占用。一般情况下,当能解决问题时,我们总是开始追求高效。这种机制明显造成资源浪费。见图二, partition 1 很忙,但是partition 3却没有被利用。图二:b 给出另外一种方案,检查是否partition为空,如果空的话,则将最近的一项任务分配进去(依然存在浪费,想想为什么?) ,基于图二:b的算法二提出: 如果partition空的话,找到input queue中大小最为接近的块填入。这种算法明显是明显对小数据块歧视,有的时候小任务反而是致命性的,需要及时处理。基于图二:b的算法三提出,可以对每次跳过的数据库计数,比如一个小任务来了,没有找到合适大小的partition来匹配,没跳过一次就+1,该算法会设立一个上线 K,跳过K次后就给该任务分配内存。


图二:内存的partition解决方案

这种将内存分割成固定大小的partition方式被用于 OS/360 IBM 架构上,被称为MFT(Multiprogramming with a Fixed number of Tasks or OS/MFT). 目前这种内存分配机制几乎销声匿迹了。
3: "钻木取火"

让多个程序驻留在内存中,也引入了其他问题:relocation (重定位) 和 protection (保护)

从图二中可以看出,不同的程序占用不同地址的内存,当一个程序需要链接时(调用其他程序或者库函数),链接器必须知道主程序的起始地址。这里需要明白编译一个程序的过程:(预处理-->编译-->汇编-->链接,这里注意,链接是在汇编之后)。假如位于100k的程序需要调用位于绝对地址100的一条汇编指令,操作系统需要调用100K+100.这就是重定位。

可能解决重定位问题的方案为:让链接器装载含有重定位地址的链表或者bitmap(这个机制可能要花上很多篇幅讲解,这里简单理解一下就好了)

protection(保护机制)就涉及到安全问题了。如果内存采用绝对地址而不是相对于寄存器的相对地址,程序可以任意的读写内存的中的字节。多用户系统下,这种保护问题尤为严峻。IBM的解决方案是:将内存划分成2K大小,并在前面加上一个4bits的代码来管理该片内存。这四个字节只有操作系统有权限改写。valgrind(http://valgrind.org/)既是采用的这种机制 (感兴趣的读者可以对valgrind加以了解)。

ok,切入正题,理解上述MMU机制,只算是理解了一半,而且这一半还没有太多实际意义。下面的内容才算是重点。

对于分时系统或者基于图形的个人计算机,主存的大小不足以装载所有的进程,因此就需要将进程放入硬盘中,需要时候进行动态读取。就引入的两种技术:swapping 机制(内存动态加载程序)和virtual memory management(虚拟内存管理)

1: swapping 机制 (内存动态加载程序)

  Swapping 是把一个程序完全加载进内存,运行一会儿,再将程序换到硬盘上。


图三 swapping

图三很好的解释了swapping,最初只有A在内存中,接着B和C从硬盘上换过来,然后A从内存中换出去到硬盘,D从硬盘换入内存。。。最后A又换进来内存执行(A 可能会被换到有不同的内存地址),与固定partition不同的是,这种机制可以更灵活的分割内存。但是这种机制可能会给内存带来很多的holes(碎片),而且,动态追踪分配内存和释放内存也成为了一个问题(可以结合C/C++的allocate 和deallocate来理解)。 当产生碎片很多时候,需要进行整理,这里就引入了memory compaction,memory compaction需要消耗很多CPU,所以不会经常触发。

swapping技术解决了固定分配partition 造成内存浪费的问题,但是也引入了动态内存分配大小的问题。如果进程占用内存大小是固定的,并且执行过程中从不改变,这个问题就好解决了,但是我们知道,程序的数据段(data segmentation)是可以增长的,如果挨着进程内存空间的内存区是碎片(hole)则可以增加内存,但是如果邻居是另外一个进程的内存空间,发生增长的进程必须移到一片更大的内存区或者被暂时换出内存到硬盘。解决这个问题的最好办法就是在每个进程的内存区附近预留一片空白内存。


图四:内存预留


图四:b值得注意,stack中保存的是局部变量和返回地址,向下生长,data中保存的是汇编指令,是向上生长,所以预留区应该在二者中间。如果预留区不够使用,则会被换出硬盘,或者移动到更大一片内存区。


paging 技术见下节....


Reference:

[1]http://blog.csdn.net/xie376450483/article/details/5728772 

[2] The MINIX book:Operating Systems Design and Implementation(Third Edition)

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值