首要,必需求论述一下这篇文章的主题是Linux内存办理中的分段和分页技能。
来回忆一下前史,在前期的计算机中,程序是直接运转在物理内存上的。换句话说,便是程序在运转的进程中拜访的都是物理地址。假如这个体系只运转一个程序,那么只需这个程序所需的内存不要超越该机器的物理内存就不会出现问题,也就不需求考虑内存办理这个麻烦事了,横竖就你一个程序,就这么点内存,吃不吃得饱那是你的作业了。可是现在的体系都是支撑多任务,多进程的,这样CPU以及其他硬件的利用率会更高,这个时分就要考虑到将体系内有限的物理内存怎样及时有用的分配给多个程序了,这个作业自身就称之为内存办理。
下面举一个前期的计算机体系中,内存分配办理的比如,以便于我们了解。
参加三个程序,程序1,2,3.程序1运转的进程中需求10M内存,程序2运转的进程中需求100M内存,而程序3运转的进程中需求20M内存。假如体系一起需求运转程序A和B,那么前期的内存办理进程大概是这样的,将物理内存的前10M分配给A, 接下来的10M-110M分配给B。这种内存办理的办法比较直接,好了,假定这个时分想让程序C也运转,一起假定体系的内存只要128M,显着依照这种办法程序C由于内存不可是不能够运转的。我们知道能够运用虚拟内存的技能,内存空间不可的时分能够将程序不需求用到的数据交流到磁盘空间上去,已达到扩展内存空间的意图。下面来看看这种内存办理方法存在的几个比较显着的问题。就像文章一开端说到的,要很深层次的掌握某个技能最好搞清楚其开展进程。
1.进程地址空间不能阻隔
由于程序直接拜访的是物理内存,这个时分程序所运用的内存空间不是阻隔的。举个比如,就像上面说的A的地址空间是0-10M这个范围内,可是假如A中有一段代码是操作10M-128M这段地址空间内的数据,那么程序B和程序C就很可能会溃散(每个程序都能够体系的整个地址空间)。这样许多恶意程序或者是木马程序能够垂手可得的破快其他的程序,体系的安全性也就得不到保证了,这对用户来说也是不能容忍的。
2. 内存运用的功率低
如上面说到的,假如要像让程序A、B、C一起运转,那么仅有的办法便是运用虚拟内存技能将一些程序暂时不必的数据写到磁盘上,在需求的时分再从磁盘读回内存。这儿程序C要运转,将A交流到磁盘上去显着是不可的,由于程序是需求接连的地址空间的,程序C需求20M的内存,而A只要10M的空间,所以需求将程序B交流到磁盘上去,而B足足有100M,能够看到为了运转程序C需求将100M的数据从内存写到磁盘,然后在程序B需求运转的时分再从磁盘读到内存,知道IO操作比较耗时,所以这个进程功率将会十分低下。
3. 程序运转的地址不能确认
程序每次需求运转时,都需求在内存中非配一块足够大的闲暇区域,而问题是这个闲暇的方位是不能确认的,这会带来一些重定位的问题,重定位的问题确认便是程序中引证的变量和函数的地址,假如有不明白童鞋能够去查查编译乐意方面的材料。
内存办理无非便是想办法处理上面三个问题,怎样使进程的地址空间阻隔,怎样前进内存的运用功率,怎样处理程序运转时的重定位问题?
这儿引证计算机界一句无从考证的名言:“计算机体系里的任何问题都能够靠引进一个中间层来处理。”
现在的内存办理办法便是在程序和物理内存之间引进了虚拟内存这个概念。虚拟内存坐落程序和屋里内存之间,程序只能看见虚拟内存,再也不能直接拜访物理内存。每个程序都有自己独立的进程地址空间,这样就做到了进程阻隔。这儿的进程地址空间是指虚拟地址。望文生义既然是虚拟地址,也便是虚的,不是现实存在的地址空间。
既然在程序和物理地址空间之间增加了虚拟地址,那么就要处理怎样从虚拟地址映射到物理地址,由于程序终究肯定是运转在物理内存中的,主要有分段和分页两种技能。
分段(Segmentation):这种办法是人们最开端运用的一种办法,根本思路是将程序所需求的内存地址空间巨细的虚拟空间映射到某个
物理地址空间。
段映射机制
每个程序都有其独立的虚拟的独立的进程地址空间,能够看到程序A和B的虚拟地址空间都是从0x00000000开端的。将两块巨细相同的虚拟地址空间和实践物理地址空间逐个映射,即虚拟地址空间中的每个字节对应于实践地址空间中的每个字节,这个映射进程由软件来设置映射的机制,实践的转化由硬件来完结。
这种分段的机制处理了文章一开端说到的3个问题中的进程地址空间阻隔和程序地址重定位的问题。程序A和程序B有自己独立的虚拟地址空间,并且该虚拟地址空间被映射到了相互不堆叠的物理地址空间,假如程序A拜访虚拟地址空间的地址不在0x00000000-0x00A00000这个范围内,那么内核就会回绝这个恳求,所以它处理了阻隔地址空间的问题。应用程序A只需求关怀其虚拟地址空间0x00000000-0x00A00000,而其被映射到哪个物理地址无需关怀,所以程序永久依照这个虚拟地址空间来放置变量,代码,不需求从头定位。
无论怎样分段机制处理了上面两个问题,是一个很大的前进,可是关于内存功率问题依然力不从心。由于这种内存映射机制依然是以程序为单位,当内存不足时依然需求将整个程序交流到磁盘,这样内存运用的功率依然很低。那么,怎样才算高功率的内存运用呢。事实上,依据程序的局部性运转原理,一个程序在运转的进程傍边,在某个时间段内,只要一小部分数据会被常常用到。所以需求愈加小粒度的内存切割和映射办法,此刻是否会想到Linux中的Buddy算法和slab内存分配机制呢,哈哈。另一种将虚拟地址转化为物理地址的办法分页机制应运而生了。
分页机制:
分页机制便是把内存地址空间分为若干个很小的固定巨细的页,每一页的巨细由内存决议,就像Linux中ext文件体系将磁盘分红若干个Block相同,这样做是别离是为了前进内存和磁盘的利用率。试想以下,假如将磁盘空间分红N等份,每一份的巨细(一个Block)是1M,假如我想存储在磁盘上的文件是1K字节,那么其他的999字节是不是浪费了。所以需求愈加细粒度的磁盘切割方法,能够将Block设置得小一点,这当然是依据所寄存文件的巨细来归纳考虑的,如同有点跑题了,我仅仅想说,内存中的分页机制跟ext文件体系中的磁盘切割机制十分类似。
Linux中一般页的巨细是4KB,把进程的地址空间按页切割,把常用的数据和代码页装载到内存中,不常用的代码和数据保存在磁盘中,仍是以一个比如来阐明,如下图:
进程虚拟地址空间、物理地址空间和磁盘之间的页映射联系
能够看到进程1和进程2的虚拟地址空间都被映射到了不接连的物理地址空间内(这个含义很大,假如有一天接连物理地址空间不可,可是不接连的地址空间许多,假如没有这种技能,程序就没有办法运转),乃至他们共用了一部分物理地址空间,这便是同享内存。
进程1的虚拟页VP2和VP3被交流到了磁盘中,在程序需求这两页的时分,Linux内核会发生一个缺页反常,然后反常办理程序会将其读到内存中。
这便是分页机制的原理,当然Linux中的分页机制的完成仍是比较复杂的,通过了也大局目录,也上级目录,页中级目录,页表等几级的分页机制来完成的,可是根本的作业原理是不会变的。
分页机制的完成需求硬件的完成,这个硬件姓名叫做MMU(Memory Management Unit),他便是专门担任从虚拟地址到物理地址转化的,也便是从虚拟页找到物理页。