您的位置:首页 > 运维架构 > Linux

【小镇的技术天梯】Linux 虚拟内存和物理内存的理解

2016-02-20 16:44 330 查看
首先,我们的环境是在32bit的系统下面的,64bit的暂不讨论啊。

第一层理解

1、每个进程都有自己独立的4G内存空间,各个进程的内存空间具有类似的结构

【这里的进程所拥有的内存空间都是虚拟内存空间】

2、一个新进程建立的时候,将会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,哪些数据在哪里,都由进程控制表中的task_struct记录,task_struct中记录中一条链表,记录中内存空间的分配情况,哪些地址有数据,哪些地址无数据,哪些可读,哪些可写,都可以通过这个链表记录

【一个进程需要的数据先从磁盘拷贝到自己的内存,这些数据在哪里,都是通过task_struct中的一条链表记录的】

3、每个进程都有分配的内存空间,都与对应的磁盘空间映射,请看下图:



现在小镇来解释下该图

1.首先我们可以看到最上层是内核虚拟存储器,其实大家需要额外了解下有关Linux内核的知识,Linux系统进程的运行是分内核态和用户态的,内核虚拟存储器用户写的代码是没有权利操作的,内核存储器是在用户写的代码调用系统内核的API的时候内核代码运行时所使用的内存空间。

2.然后第二层是用户栈,这些内存空间是给代码运行时所产生的中间数据使用的。

3.然后是共享库的存储器映射区域,这边小镇也不知道具体的原理,但是大概的意思应该是映射的共享库在物理内存中的地址。

4.运行时堆,这个大家学过c语言应该是很好理解的,malloc手动开辟的内存空间就是在运行时堆中。

5.直接按照字面的理解就行了,读写段和只读段,跟本文没有太大的联系。


大家一定非常的疑惑,啊?一个进程4G的内存空间?那么问题就来了,计算机明明没有那么多的内存,如果说建立一个进程,就要把磁盘上的程序文件拷贝到进程对应的内存中去,对于一个程序对应的多个进程这种情况,浪费内存!

第二层理解

1.每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理内存地址

2.所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射并存储到物理内存上。

【这样有一个好处,就是所谓的进程内存独立性】

3.进程要知道哪些内存地址上的数据在物理内存上,哪些不在,还有在物理内存上的哪里,需要用页表来记录

4.页表的每一个表项分两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址(如果在的话)

【内核维护的一个数据结构,用来映射虚拟内存和物理内存】

5.当进程访问某个虚拟地址,去看页表,如果发现对应的数据不在物理内存中,则缺页异常

6.缺页异常的处理过程,就是把进程需要的数据从磁盘上拷贝到物理内存中,如果内存已经满了,没有空地方了,那就找一个页覆盖,当然如果被覆盖的页曾经被修改过,需要将此页写回磁盘。

从我看的文章上面拷贝了一张图下来,大家看看:



优点:

1.既然每个进程的内存空间都是一致而且固定的,所以链接器在链接可执行文件时,可以设定内存地址,而不用去管这些数据最终实际的内存地址,这是有独立内存空间的好处。

【其实也是遵循了一种编程思想,叫低内聚,自己的事情自己管,真正的物理内存地址让专门的映射函数去完成】

2.当不同的进程使用同样的代码时,比如库文件中的代码,物理内存中可以只存储一份这样的代码,不同的进程只需要把自己的虚拟内存映射过去就可以了,节省内存

【这个才是最主要的优点,真正意义上的节省了内存空间。】

3.在程序需要分配连续的内存空间的时候,只需要在虚拟内存空间分配连续空间,而不需要实际物理内存的连续空间,可以利用碎片。

【只要虚拟内存空间联系方便编程利用就可以了,其他的让映射函数完成吧,啊哈哈哈哈】

另外,事实上,在每个进程创建加载时,内核只是为进程“创建”了虚拟内存的布局,具体就是初始化进程控制表中内存相关的链表,实际上并不立即就把虚拟内存对应位置的程序数据和代码(比如.text .data段)拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射就好(叫做存储器映射),等到运行到对应的程序时,才会通过缺页异常,来拷贝数据。还有进程运行过程中,要动态分配内存,比如malloc时,也只是分配了虚拟内存,即为这块虚拟内存对应的页表项做相应设置,当进程真正访问到此数据时,才引发缺页异常。

【这个也很好理解对吧,只有要用到这个数据的时候才会发生缺页异常区拷贝数据到物理内存,很好的节省了计算机的性能,还没用到的东西干嘛要急着初始化对吧!】

请看下图:



这就是上面说的按需映射,映射函数是mmap函数,我可能会专门开一篇文章讲一下该函数,先找有效位的值,有的话就去物理地址找,没有的话就产生缺页异常去映射一个。

mmap是用来建立从虚拟空间到磁盘空间的映射的,可以将一个虚拟空间地址映射到一个磁盘文件上,当不设置这个地址时,则由系统自动设置,函数返回对应的内存地址(虚拟地址),当访问这个地址的时候,就需要把磁盘上的内容拷贝到内存了,然后就可以读或者写,最后通过manmap可以将内存上的数据换回到磁盘,也就是解除虚拟空间和内存空间的映射,这也是一种读写磁盘文件的方法,也是一种进程共享数据的方法 共享内存

在内核态申请内存比在用户态申请内存要更为直接,它没有采用用户态那种延迟分配内存技术。内核认为一旦有内核函数申请内存,那么就必须立刻满足该申请内存的请求,并且这个请求一定是正确合理的。相反,对于用户态申请内存的请求,内核总是尽量延后分配物理内存,用户进程总是先获得一个虚拟内存区的使用权,最终通过缺页异常获得一块真正的物理内存。

【哇擦!内核的权限就是大呀!要内存就必须直接有,领导的命令必须要执行】

下面还有很多物理内存的知识,不过过于复杂,关键是没有必要学,对学习strace的帮助不大,所以小镇就学啦,哎嘿嘿,小镇就是懒!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: