Linux内核之内存管理知识结构

目录

Linux内核源码分析架构图

一、虚拟内存地址空间布局

1、用户空间

2、内核空间

3、硬件层面

4、虚拟地址空间布局

二、SMP/NUMA架构

三、伙伴系统及算法

1、基本伙伴分配器

2、分区伙伴分配器

四、块分配器(Slab/Slub/Slob)

1、基本概念

2、slab块分配器原理

3、系统编程接口

4、内存缓存的数据结构

5、计算slab长度及着色

6、每处理器数组缓存

7、slab分配器支持NUMA体系结构

五、vmalloc/kmalloc系统调用

1、编程接口

2、数据结构


C/C++Linux服务器开发/后台架构师【零声教育】-学习视频教程-腾讯课堂

Linux内核源码分析架构图

Linux内核之内存管理知识结构_第1张图片

Linux内核之内存管理知识结构_第2张图片

一、虚拟内存地址空间布局

1、用户空间

        应用程序使用malloc()申请内存,使用free()释放内存,malloc()/free()是glibc库的内存分配器ptmalloc提供的接口,ptmalloc使用系统调用brk/mmap向内核以页为单位申请内存,然后划分成小内存块分配给用户应用程序。

2、内核空间

        内核空间的基本功能:虚拟内存管理负责从进程的虚拟地址空间分配虚拟页,sys_brk用来扩大或收缩堆,sys_mmap用来在内存映射区域分配虚拟页,sys_munmap用来释放虚拟页。

        页分配器负责分配物理页,当前使用的页分配器是伙伴分配器。内核空间提供把页划分成小内存块分配的块分配器,提供分配内存的接口kmalloc()和释放内存接口kfree()。块分配器:SLAB/SLUB/SLOB。

3、硬件层面

        处理器包含一个称为内存管理单元(MemoryManagementUnit,MMU)的部件,负责把虚拟地址转换成物理地址。内存管理单元包含一个称为页表缓存(TranslationLookasideBuffer,TLB)的部件,保存最近使用的页表映射,避免每次把虚拟地址转换物理地址都需要查询内存中的页表。

4、虚拟地址空间布局

a、虚拟地址空间划分

Linux内核之内存管理知识结构_第3张图片

        以ARM64处理器为例:虚拟地址的最大宽度是48位。内核虚拟地址在64位地址空间顶部,高16位全是1,范围是[0xFFFF000000000000,0xFFFFFFFFFFFFFFFF]。用户虚拟地址在64位地址空间的底部,高16位全是0,范围是[0x0000000000000000,0x0000FFFFFFFFFFFF]。

在编译ARM64架构的Linux内核时,可以选择虚拟地址宽度:

l选择页长度4KB,默认虚拟地址宽度为39位;

l选择页长度16KB,默认虚拟地址宽度为47位;

l选择页长度64KB,默认虚拟地址宽度为42位;

l选择48位虚拟地址。

        在ARM64架构linux内核中,内核虚拟地址用户虚拟地址宽度相同。所有进程共享内核虚拟地址空间,每个进程有独立的用户虚拟地址空间,同一个线程组的用户线程共享用户虚拟地址空间,内核线程没有用户虚拟地址空间。

b、用户虚拟地址空间布局

        进程的用户虚拟地址空间的起始地址是0,长度是TASK_SIZE,由每种处理器架构定义自己的宏TASK_SIZE。ARM64架构定义宏TASK_SIZE如下所示:

        32位用户空间程序:TASK_SIZE的值是TASK_SIZE_32,即0x10000000,等于4GB。

        64位用户空间程序:TASK_SIZE的值是TASK_SIZE_64,即2的VA_BITS次方字节,VA_BITS是编译内核时选择的虚拟地址位数。

Linux内核之内存管理知识结构_第4张图片

Linux内核使用内存描述符mm_struct描述进程的用户虚拟地址空间,主要核心成员如下

Linux内核之内存管理知识结构_第5张图片

mm_struct结构体类型常用的成员解释请看源码分析即可。

c、进程的进程描述和内存描述符关系如下图所示:

Linux内核之内存管理知识结构_第6张图片

一个进程的用户虚拟地址空间包括区域:代码段、动态库的代码段、数据段、未初始化数据段、代码段、未初始化代码段、存放在栈底部的环境变量、参数字符串等等。

Linux内核之内存管理知识结构_第7张图片

d、内核地址空间布局

Linux内核之内存管理知识结构_第8张图片

二、SMP/NUMA架构

1、SMP(SymmetricMulti-processing)对称多处理器结构。比如服务器中多个CPU对称运行,没有主次和从属关系,CPU共享相同的物理内存。SMP也称为一致存储器访问结构(UMA:UniformMemoryAccess)。

Linux内核之内存管理知识结构_第9张图片

三、伙伴系统及算法

        Linux内核初始化完毕后,使用页分配器管理物理页,当前使用的页分配器就是伙伴分配器,伙伴分配器的特点是管理算法简单且高效。

1、基本伙伴分配器

连续的物理页称为页块(pageblock),阶(order)是页的数量单位,2

的n次方个连续页称为n阶页块,满足如下条件的两个n阶页块称为伙伴

(buddy)。

l1)两个页块是相邻的,即物理地址是连续的;

l2)页块的第一页的物理面页号必须是2的n次方的整数倍;

l3)如果合并(n+1)阶页块,第一页的物理页号必须是2的括号(n+1)次方的整数倍。

伙伴分配器分配和释放物理页的数量单位也为阶(order)。

以单页为说明,0号页和1号页是伙伴,2号页和3号页是伙伴。1号页和2号页不是伙伴?因为1号页和2号页合并组成一阶页块,第一页的物理页号不是2的整数倍。

2、分区伙伴分配器

        内存区域的结构体成员free_area用来维护空闲页块,数组下标对应页块的除数。结构体free_area的成员free_list是空闲页块的链表,nr_free是空闲页块的数量。内存区域的结构体成员managed_pages是伙伴分配器管理的物理页的数量。

2.1)内存区域数据结构分析如下:

Linux内核之内存管理知识结构_第10张图片

2.2)区域水线数据结构分析

        首选的内存区域在什么情况下从备用区域借用物理页?此问题从区域水线讲解深入理解,每个内存区域有3个水线。

高水线(HIGH):如果内存区域的空闲页数大于高水线,说明该内存区域的内存充足;

低水线(LOW):如果内存区域的空闲页数小于低水线,说明该内存区域的内存轻微不足;

c.最低水线(MIN):如果内存区域空闲页数小于最低水线,说明该内存区域的内存严重不足。

Linux内核之内存管理知识结构_第11张图片

四、块分配器(Slab/Slub/Slob)

1、基本概念

        Buddy提供以page为单位的内存分配接口,这对内核来说颗粒度还太大,所以需要一种新的机制,将page拆分为更小的单位来管理。Linux中支持的主要有:slab、slub、slob。其中slob分配器的总代码量比较少,但分配速度不是最高效的,所以不是为大型系统设计,适合内存紧张的嵌入式系统。

2、slab块分配器原理

        slab分配器的作用不仅仅是分配小块内存,更重要的作用是针对经常分配和释放的对象充当缓存。slab分配器的核心思路是:为每种对象类型创建一个内存缓存,每个内存缓存由多个大块组成,一个大块是由一个或多个连续的物理页,每个大块包含多个对象。slab采用面向对象的思想,基于对象类型管理内存,每种对象被划分为一类,比如进程描述符task_struct是一个类,每个进程描述符实例是一个对象。如下图所示为内存缓存的组成结构:

Linux内核之内存管理知识结构_第12张图片

        slab分配器在某些情况下表现不太优先,所以Linux内核提供两个改进的块分配器。

        在配备大量物理内存的大型计算机上,slab分配器的管理数据结构的内存开销比较大,所以设计了slub分配器;

        在小内存的嵌入式设备上,slab分配器的代码过多、相当复杂,所以设计一个精简slob分配器。

目前slub分配器已成为默认的块分配器。

3、系统编程接口

创建专用的内存缓存编程接口如下:

la.创建内存缓存kmem_cache_create

lb.指定内存缓存分配kmem_cache_alloc

lc.释放对象kmem_cache_free

ld.销毁内存缓存keme_cache_destroy

备注:参考代码实战模块案例。4、内存缓存的数据结构

4、内存缓存的数据结构

Linux内核之内存管理知识结构_第13张图片

5、计算slab长度及着色

a、计算slab:函数calculate_slab_order负责计算slab长度,从0阶到kmalloc()函数支持最大除数KMALLOC_MAX_ORDER。

Linux内核之内存管理知识结构_第14张图片

b、着色

        slab是一个或多个连续的物理页,起始地址总是页长度的整数倍,不同slab中相同偏移的位置在处理器一级缓存中的索引相同。如果slab的剩余部分的长度超过一级缓存行的长度,剩余部分对应的一级缓存行没有被利用;如果对象的填充字节的长度超过一级缓存行的长度,填充字节对应的一级缓存行没有被利用。这两种情况导致处理器的某些缓存行被过度使用,另一些缓存行很少使用。

6、每处理器数组缓存

        内存缓存为每个处理器创建一个数组缓存(结构体array_cache)。释放对象时,把对象存放到当前处理器对应的数组缓存中;分配对象的时候,先从当前处理器的数组缓存分配对象,采用后进先出(LastInFirstOut,LIFO)的原则,这种做可以提高性能。

Linux内核之内存管理知识结构_第15张图片

7、slab分配器支持NUMA体系结构

内存缓存针对每个内存节点创建一个kmem_cache_node实例视图如下:

 Linux内核之内存管理知识结构_第16张图片

五、vmalloc/kmalloc系统调用

        当设备长时间运行后,内存碎片化,很难找到连续的物理页。在这种情况下,如果需要分配长度超过一页的内存块,可以使用不连续页分配器,分配虚拟地址连续但是物理地址不连续的内存块。在32位系统中不连分配器还有一个好处:优先从高端内存区域分配页,保留稀缺的低端内存区域。

1、编程接口

A、内核提供API函数接口

        vmalloc:分配不连续的物理地址空间,但虚拟内存地址是连续的。配套:vfree(...)。作用:释放vmalloc分配的内存地址。

B、内核提供API函数接口

        kmalloc:分配物理连续的内存地址(则虚拟地址自然连续,基于slab)。配套:kfree(...)。作用:释放kmalloc分配的内存地址。

2、数据结构

Linux内核之内存管理知识结构_第17张图片

每个虚拟内存区域对应一个vmap_area实例;

每个vmap_area实例关联一个vm_struct实例;六、页表及页表缓存原理

1、【页】:为进程中的块。

2、【页框】:为内存中的块。

3、【页表】:为特殊数据结构,存放系统空间的页表区域(存放逻辑页与物理页帧的对应关系)。每个进程都有自己的页表,PCB表中有指针指向页表。

4、【页表项】

l逻辑地址(页号,偏移量)(逻辑地址就是虚拟地址)。比如二级页表的页表项如下:

Linux内核之内存管理知识结构_第18张图片

5、【页表缓存】:CPU内存管理单元(MMU),处理器制造厂商在内存管理单元中增加的一个高速缓存(称为页表缓存)。作用将虚拟地址转换成物理地址。

Linux内核之内存管理知识结构_第19张图片

        页表由多个页表项构成,页表中每一行为一个页表项。页框号:专门用于存储虚拟页面对应的物理页面。页表项由硬件设计。

        MMU:CPU获取虚拟地址-->MMU查询页目录-->MMU查询页表-->MMU获取页表项-->MMU获取到物理页面编号-->MMU获取物理地址。

函数mempool_alloc(mempool_t*pool,gfp_tgfp_mask)

功能:直接从内存池当中分配一个内存元素。

函数page_address()

功能:获取物理页的逻辑地址。

 

你可能感兴趣的:(linux服务器高级框架,linux,服务器)