上讲中,讲解了linux的物理内存管理,这讲进行虚拟内存管理的讲解。
咱们程序猿们常常讲“我new(malloc)了多少内存”,实际上咱们使用的new(malloc)申请的内存是“虚拟内存”,更确切的说,咱们程序猿(app程序猿,不是写内核的)所能操控的都是“虚拟内存”。
什么是“虚拟内存”?就是这个“内存”不是指真正内存条的,而是操做系统给你开的一个空头支票。尼玛?这是怎么回事?是否是有点晕?没事,我给你们举个例子。
假设有个银行,银行只有1000w,谁须要钱就向银行借,后续只需还本金不用还利息(现实中要有这么好就行了)。假设每一个人一辈子下来,银行就给他开个1000w的支票且不能直接使用支票,但银行并不会立刻给他1000w,等到他用钱的时候,银行就给他实际须要的数目,若是银行暂时没有那么多,就让他等等。可是通常人都不会一次性须要那么多钱,这样基本上每一个人取钱的时候,银行通常都会知足,让每一个人感受他都有1000w可用。
操做系统就好像上面例子中的银行,物理内存就好像钱,虚拟内存就是银行开出的支票。当咱们app启动时,操做系统就分给它4G虚拟内存(32位),等到app实际须要内存时,操做系统才分配真正的物理内存给它,这样每一个app都感受它拥有4G内存。
为何要有虚拟内存呢?
个人理解是:方便管理,安全。
若是每一个app直接去操做物理内存,因为只有一块物理内存,那每一个程序起始的物理地址就不同,比较麻烦,有了虚拟内存,你们的起始地址都同样,方便管理;再就是安全,有了虚拟内存,app只能操做虚拟内存,物理内存由内核统一安排,这样不至于某个app出了问题,把别的app的内存数据给覆盖,影响另外一个app。
如今应该看看虚拟内存的地址空间了(32位的):
java
虚拟地址空间整体上分为两大部分:内核空间(1G)和用户空间(3G)。咱们的app能操做的虚拟地址就是用户空间部分。
内核空间:内核空间大致上又可分为两部分,3G~3G+896M,这部分到物理地址的映射是写死的,毕竟总得有内核本身的代码也是要加载到物理内存中才能执行啊!(这也说明,程序中的初始化是一个程序很是重要的部分,从此遇到开源程序,先看它的初始部分,不然会看晕的)
用户空间:用户空间通常有3G,又可细分为:stack、mmap、heap、bss、data、text等部分。
stack:比较常见,就是咱们平时说的栈,内存的释放不须要咱们管理;
mmap:这里通常是将咱们要读的文件映射进来,实现文件的直接读写;
heap:就是咱们日常说的堆, c/c++中的new、malloc就是在这个范围内分配,须要咱们程序管理释放。
bss:一般是指用来存放程序中未初始化的全局变量的一块内存区域。
data:一般是指用来存放程序中已初始化的全局变量的一块内存区域。
text:一般是指用来存放程序执行代码的一块内存区域。
用户空间的这些划分都是逻辑上的,内核代码中使用memory region对象实际管理整个虚拟地址空间,结构为vm_area_struct。一个进程中可能有多个memory region,每一个memory region表明了一段地址空间范围。全部memory region以链表的形式链接在一块儿(同时也以红黑树的结构存储,为了快速定位一个具体的地址)。
memory region和上面将的stack、heap等是模糊对应的,例如bss、heap等有可能在同一个memory region上。
讲到这里,基本上把虚拟内存与物理内存的关系,以及虚拟内存的做用将清楚了(有不清楚的,能够下来直接找我)。如今就应该讲讲内核中是怎么把虚拟内存映射到物理内存的(毕竟支票没法使用)。
通过编译后的程序中的地址都是虚拟地址,cpu再执行这个命令以前必须把虚拟地址转换为物理地址,这个转换使用的就是页表,每一个app都有本身的页表。linux为了高度抽象化,规定页表有四级:
页全局目录PGD、页上级目录PUD、页中间目录PMD和页表PT。以下图所示:(具体转换不作介绍,网上比较多,你们能够查阅)
linux
讲到这里,你们彷佛感受到:一个程序要使用内存,必须先申请虚拟内存,而后操做系统才给你分配物理内存。you are right!!!程序中分配虚拟内存的接口为new(c++)、malloc(c)。java中不须要咱们本身操做,是由jvm向操做系统申请的。
本贴只将了基本性的概念,具体你们能够分块查询,学习。c++