虚拟内存的作用

发表于:2014-01-22来源:IT博客大学习作者:杨镇锋点击数: 标签:虚拟内存
直译是虚拟内存,对于WINDOWS下的用户,直观的感受是,在硬盘上开辟一片区域当内存用。而LINUX下的用户,直观感受是,一个进程的内存占用,分虚拟内存与物理内存。

  直译是虚拟内存,对于WINDOWS下的用户,直观的感受是,在硬盘上开辟一片区域当内存用。而LINUX下的用户,直观感受是,一个进程的内存占用,分虚拟内存与物理内存。

  虚拟内存的作用,个人理解,主要有几个:

  (1)简化开发,每个进程都可以认为自己占有整个内存,这对多任务系统很重要,早期有些系统,甚至需要使用相对地址,再根据代码载入内存的基准地址,算出真正要访问哪个内存地址

  (2)利用多级存储系统,把硬盘或别的存储介质当内存使用,在内存不足的系统,这点很重要,线上服务的机器,为了确保响应速度,一般会关闭这个功能

  (3)节省内存,用户往往事先不知道需要用多少内存,所以会申请一个很大的数组,会造成浪费,所以刚开始的时候,是只分配虚拟内存,当用到的时候才分配物理内存

  对于第二点,对于多级存储系统,其实一般就两级,内存跟硬盘,还需要有一个淘汰策略决定什么内存放哪。如果有三种存储介质,比如SSD,LINUX 在这种情况下会出错。如果内存不够,而硬盘上开了SWAP分区(也就是虚拟内存了),LINUX的行为是,先把数据读到SWAP,然后再从SWAP读数据,SSD白搭了。

  具体实现的时候,是把内存分页,一个页面一般是4K,当访问一个还没分配出来的内存单元的时候,会把该内存单元所在的整个页面都分配出来。这样一个好处是,逻辑上连续的虚拟地址,可以映射成不连续的物理地址。

  MMAP实现的时候,就是以页为单位分配内存。以前出过一次CORE,是用MMAP读文本文件,用了很多字符串函数,默认输入数据是以'\0'结束的。问题是文本文件里是不可能有'\0'的,这个'\0'其实是mmap读入数据的时候,对一个内存页里空的数据都置0。运气不好的时候,文件大小正好是 4K的整数次倍,后面就没有'\0',就一直往后读,最后CORE了

  不是所有的内存都分页的,有部分内存是被OS强制占用,以确保性能

  还有一种实现方式是把内存分段,不过已经不常用了。

  在运行过程中,需要把虚拟地址转换为物理地址。程序运行的时候,如果访问了非法内存,这时候会CORE掉,并且报SEG ERROR,就是在这个阶段探测出来的。虽然内存是分页的,但是在上层还是会维护一些段的结构以标记哪些内存是可以访问,哪些不可以。

  为了提高速度,在CPU里有个部件专门负责转换,称为MMU。

  想像一下最简单的转换算法,虚拟内存地址的低位表示页内的偏移,高位表示页ID,在内存里开辟一片连续区域,存储每一个页面对应的物理页面地址。

  这个方法主要的问题是,太耗内存了。虚拟地址空间往往很大,会超过物理内存的大小,假设用40个BIT表示,一个页面4K,则有2^28个页面,这个消耗很大。

  虚拟地址的空间很大,但是最终会用到的很少,不会超过RAM的大小。解决方案是用多层表。先把整个空间分成N段(N比较小),如果这一段里面有数据,则把这一分再切割成N份,继续递归,一般只切割4层。对于一大片没用到的空间,在表里很高的一层就统计出没有数据,不用分配出下一层的表了。为了减少多层表里节点的内存开销,上层需要把虚拟地址尽可能地连续。

  这个解决方案明显的问题是,作一次转换需要多次访问内存,就算把这个表放在CACHE里,多次访问的开销还是明显的,因为这四次访问不能并行,每一步都要等上一步有结果才能进行。

  所以解决方案是增加一个CACHE,称为TLB,把转换的最终结果CACHE住。

  为了提高TLB的命中率,最简单的方法是,减少需要CACHE的数据,就是把内存页增大。当然这样可能会造成内存浪费,所以一般不这样做。

  在MISS之后,为了减少开销,可以的把多层表的层数减少,这样步骤会变少,但是内存占用也可能增加。

  前面说了这么多理论的。在作代码优化的时候,大部分的教材是不会说怎么检测TLB MISS的,用VTUNE的时候,一般也不会这样提示。用VTUNE是可以检测TLB的开销的,但是一般来说这部分开销不会是主要的。假设上层是顺序访问虚拟内存,这时候读4K数据,至多发生一次TLB MISS,这个开销占比不大。如果是随机访问,从概率上可以分析出,数据CACHE MISS的次数会比TLB MISS要多很多。而且在这种情况下,作优化,一般也就是把数据连续存放,这时候TLB的CACHE MISS也变小了。目前没碰到过需要对TLB作优化才能解决的问题。以后用VTUNE的时候,可以专门分析一下。

  除了多层表,还有其他解决方案的,比如用HASH表把所有虚拟内存页跟物理内存页的映射记录下来。HASH的主要问题是数据局部性不好。当顺序访问虚拟内存的时候,查表的时候,却可能访问相距很远的两个节点。

原文转自:http://blogread.cn/it/article/3085