虚拟内存和物理内存的区别和联系
在计算机的远古时代其实还没有虚存机制,程序指令所访问的内存地址就是物理内存地址. 也就是不得不把程序的全部装进内存当中,
然后运行.
首先我们知道的物理内存其实就是插在计算机主板内存槽上的实际物理内存,CPU可以直接进行寻址. 物理内存的容量是固
定的,但是寻址空间却取
决
于
cpu地址线条数,如32位机,则寻址空间为2^32 = 4G.所以最大支持4G的寻址空间.即使我们插入了16G的
内存条,我们也只是拥有4G内存.我们其
实发现无论好多事物只要是一个一成不变的就会慢慢的出现各种问题,然后就得找到它的解决
方法.这不物理内存也出现问题了.
现在假设没有虚存机
制,我们要运行一个程序,就不得不把程序的全部装入内存当中,然后运行.
这个时候就会出现问题.
1.现在有多个程序需要运行,但是内存空间不足了,就需要将其他程序暂时拷贝到硬盘当中,然后将新的程序装入
内存运
行.由于
大量的数据装入装
出,内存的使用效率会十分Low
.
2.由于程序都是直接访问物理内存的,所以一个进程可以修改其他进程的内存数据,甚至修改内核地址空间中的数据
3.因为内存地址是随机分配的,所以程序运行的地址也是不正确的.
我们可以看出来,"微笑三连"的无奈了吧. 这就是早期没有虚存机制的无奈.但是我们怎么会被困难打倒所以前辈们经过不懈努力找到
了虚拟内
存这
种
骚操作了.那么何为虚拟内存呢? ??
进程创建加载的时候,自身感知获得了一个连续的内存地址空间,而实际上内核只是分配了一个逻辑上的虚拟内存空间,并且对虚拟
内存和磁盘通过
mmap做映射关系,对虚拟内存和物理内存的映射关系; 等到程序真正运行的时候,需要某些数据,并且不在虚拟内
存中,才会触发缺页异常,进行数据
拷贝.说白了虚拟内存就是磁盘上面的一片空间. 上面的解释比较官方,我再说的接地气一点,
内核会将暂时不用的内存块信息写到磁盘当中,这样一
来,物理内存得到了释放,这块内存可以用于其他目的,当需要用到原始内容
的时候,这些信息会被重新从磁盘当中读取到物理内存当中.
对于32位系统,寻址指针为4字节,对应的虚拟地址空间为0 ~ 2^32,既0-4G
对于64位系统,寻址指针为8字节,对应的虚拟地址空间为0 ~ 2^64,既0-16G
Linux下虚拟内存的结构图:
以32位机为例,我们上图看到的进程虚拟存储器占用3G虚拟内存空间,内核虚拟存储器占用1G的虚拟内存空间. 在缓存原理当中,换入
/换出是以块为
最小单位.在内存管理时,页是地址空间的最小单位.
虚拟地址空间划分为多个固定大小的虚拟页(vp),物理地址空间
(DRAM)内存划分为多个固定大小
的物理页(pp).虚拟页和物理页的大小是一样的,通常为4KB.
那么接下啦重点来啦!! 虚拟内存的运行
原理是什么????? 我借用一下我以前写共享
内存当中博客上面的图,当中你还顺便可以看看进程间通信方式中共享内存的样子.
我们看到我们所拿到的虚拟内存,通过页表让虚拟地址映射到物理地址上面,那么这个页表到底是一个何方神圣呢???
页表的基本概念
页表是一种数组结构,存放着各个虚拟页的状态,是否映射,是否缓存. 进程要知道那些内存地址上的数据在物理内存上,那些不在
,还有物理内存
上的哪里需要用页表来记录.
页表的每一个表项分为两部分,第一部分记录此页是否在物理内存上,第二部分记录物
理内存页的地址(如果在的话)
当进程访问某个虚拟地址,去查看页表的时候如果发现对应的数据不在物理内存中,则发生缺页异常.
但是我们马上就要使用这个数据啊.所以我们需
要尽快解决掉缺页异常.
缺页异常的处理过程:就是把进程需要的数据从磁盘上面拷贝到物理内存中,如果物理内存已经满了,没有空地方了,那就找一个页覆
盖,当然如果被
覆盖的页曾经被修改过,需先将此页写回磁盘.
页表的状态>>
如果页表的有效位置为1,那么说明虚拟地址存储的内容存储在物理页当中.
如果页表的有效位置为0,那么说明虚拟存储的内容没有存储在物理页中, 发生了缺页异常,需要调用处理掉缺页异常.
虚拟地址的工作原理
首先我们来看看页表的工作原理,如果我们懂了页表的工作原理那么整个映射过程也就明白了.
好,那我们开始分析它的过程:
1)我们的cpu想访问虚拟地址所在的虚拟页(VP3),根据页表,找出页表中第三条的值.判断有效位. 如果有效位为1,DRMA缓存命中
,根据物理页号,
找
到物理页当中的内容,返回.
2)若有效位为0,参数缺页异常,调用内核缺页异常处理程序.内核会选择一个物理页作为牺牲页,将该页的内容刷新到磁盘空
间当中.然后把VP3映
射
的磁盘文件缓存到该物理页
上面.然后页表中第三条,有效位变
成1,第二部分存储上了可以对应物理内
存页的地址的内容.
3)缺页异常处理完毕后,返回中断前的指令,重新执行,此时缓存命中,执行1.
4)将找到的内容映射到告诉缓存当中,CPU从告诉缓存中获取该值.结束.
Linxu中虚拟内存的机制
Linux把虚拟内存划分成区域area的集合,一个area包括连续的多个页.
area的数据结构如下所示:
1)内核为每个进程维护了一个单独的任务结果 task_struct.
2)task_struct当中的 struct mm_struct *mm指针,指向了mm_strcut.该结构描述虚拟内存的运行状态.
3)mm_strcut的pgd指针指向进程的一级页面的基地址. mmap指针,指向vm_area_struct链表.
4)vm_area_struct描述area的结构,vm_start表示area的开始位置,vm_end表示area的结束位置. vm_port表示area内的读写
权限
,vm_flags表示area内的页面是进程私有还是共享的,vm_next指向的是下一个area节点.
总结
1.既然每一个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去
管这些数据最
终实际的内存地
址,这里有独立内存空间的好处.
2.当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同进程只需要
把自己的虚拟
内存映射过去就
可以
了,节省内存.
3.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,
可以利用内
存碎片.
4.使用页面调度算法,不会造成大量的数据装进装出,提高了内存的效率.
事实上在每个进程创建加载时,内核只是为进程"创建"了虚拟内存的布局,具体就是初始化进程控制表中相关的链表,实际上并不是
立即就把虚拟内存
对应位置的程序数据和代码拷贝给物理内存,只是建立好虚拟内存和磁盘空间之间的映射就好(存储器映射),等到
运行到对应的程序时,才会通过缺页
异常来拷贝数据.还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存
,既为这块虚拟内存对应的页表项做相应的设置,当进
程真正访问到此数据的时候,才会发生缺页异常.