最近因为项目中需求对linux的内存管理做了一些研究,现记录下来,一是为了自己以后方便深入学习,二是分享给那些需要的人,以便大家共同学习。
linux内存管理总体上分为两块:物理内存管理和虚拟内存管理。本篇博客就从这两块进行分析。
先说物理内存,这个很好理解,就是我们主机中那块内存条,也就是大家口中平常所说的“我的内存是4G”中所指的那个“内存”。现在问题就来了:内存条是物理的,linux内核是个程序,说linux内核管理它,似乎中间缺少了些什么。对!就是介质的“程序化”。linux内核要管理介质,首先要把介质用程序中的一个数据结构表示吧,这样linux内核才能用它啊。先看物理介质“程序化”后的结构图:
在内核中,一块均匀的物理内存介质,使用一个pg_data_t,如上图所示。为了方便管理,一个pg_data_t又一般分为三个区域:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。
ZONE_DMA:在IA-32计算机中一般是16M,适合DMA使用。
ZONE_NORMAL:表示可直接线性映射到内核段的内存域。
ZONE_HIGHMEM:这段物理内存是和以后讲的虚拟内存是动态映射的。(是不是感觉有点晕,没关系,后面讲完虚拟内存就可以对应上了。)
讲到这里,也只是物理内存逻辑上的一种划分,实际中它真正怎么高效管理起来,比如你现在需要2M物理内存,linux内核马上就能分配给你,等会你又要100M内存,一会你用完了要释放,其他进程又要使用等等。现实是linux对这种管理高效到我们几乎感觉不到,这就得意于“伙伴系统”了。先看图:
linux内核会将每个zone中的可用物理内存,按照2的0次方、1次方等等划分(每个块的物理地址是连续的),并按上图所示组织起来,当某用户需要一块内存时,找到刚好能满足它的块。例如,你需要一个1M内存,那就是4KB*2^8,linux内核就到上图中左侧为8对应的后面链表中去查找,如果找到了,就分配给你,没有就到9对应的里面去找,找到后会将2^9分为2^8和2^1两部分,2^8分配给你,2^1部分放到1对应的链表中。如果1对应的链表中可以合并为2^2,那么就会合并并将这个块调整到2对应的链表中。
“伙伴系统”管理的最小单位是“页帧”(4KB),但实际上我们应用程序中使用的内存分配函数(比如c语言中的malloc)等是按字节计算的,4KB对我们来说太大,会造成“内部碎片”导致内存浪费,此时linux又有另外一种秘密武器——slab分配器,它就是专门针对这种小内存需求进行物理内存分配的。
关于物理内存管理,我就点到为此,本篇只给大家一个较为系统的介绍,方便大家深入学习时的一个方向确定,各个细节可以再找其他资料详细参考学习。