本文是笔者 slab 系列的最后一篇文章,为了方便大家快速检索,先将相关的文章列举出来:
在之前的这四篇文章中,笔者详细的为大家介绍了 slab 内存池的整体架构演化过程,随后基于这个演化过程,介绍了整个 slab alloactor 体系的创建,内存分配,内存释放以及销毁等相关复杂流程在内核中的实现。
我们知道 slab 内存池是专门为了应对内核中关于小内存分配需求而应运而生的,内核会为每一个核心数据结构创建一个专属的 slab 内存池,专门用于内核核心对象频繁分配和释放的场景。比如,内核中的 task_struct 结构,mm_struct 结构,struct page 结构,struct file 结构,socket 结构等等,在内核中都有一个属于自己的专属 slab 内存池。
而之前介绍的这些都属于专有的 slab 内存池,slab 在向伙伴系统申请若干物理内存页 page 之后,内核会按照需要被池化的专有数据结构在内存中的布局 size,从这些物理内存页中划分出多个大小相同的内存块出来,然后将这些划分出来的内存块统一交给其所属的 slab 内存池管理。每个内存块用来专门存储特定结构的内核对象,不能用作其他用途。
内核中除了上述这些专有内存的分配需求之外,其实更多的是通用小内存的分配需求,比如说,内核会申请一些 8 字节,16 字节,32 字节等特定尺寸的通用内存块,内核并不会限制这些通用内存块的用途,可以拿它们来存储任何信息。
内核为了应对这些通用小内存的频繁分配释放需求,于是本文的主题 —— kmalloc 内存池体系就应用而生了,在内核启动初始化的时候,通过 kmem_cache_create 接口函数预先创建多个特定尺寸的 slab cache 出来,用以应对不同尺寸的通用内存块的申请。
struct kmem_cache *
kmem_cache_create(const char *name, unsigned int size, unsigned int align,
slab_flags_t flags, void (*ctor)(void *))
我们可以通过 kmem_cache_create 函数中的 size 参数来指定要创建的通用内存块尺寸,相关的创建流程细节,感兴趣的同学可以回看下这篇文章 《从内核源码看 slab 内存池的创建初始化流程》。
kmalloc 内存池体系的底层基石是基于 slab alloactor 体系构建的,其本质其实就是各种不同尺寸的通用 slab cache。
我们可以通过 cat /proc/slabinfo
命令来查看系统中不同尺寸的通用 slab cache:
kmalloc-32
是专门为 32 字节的内存块定制的 slab cache,用于应对 32 字节小内存块的分配与释放。kmalloc-64
是专门为 64 字节的内存块定制的 slab cache,kmalloc-1k
是专门为 1K 大小的内存块定制的 slab cache 等等。那么 kmalloc 体系究竟包含了哪些尺寸的通用 slab cache 呢 ?
1. kmalloc 内存池中都有哪些尺寸的内存块
本文内核源码部分基于 5.4 版本讨论
内核将这些不同尺寸的 slab cache 分类信息定义在 kmalloc_info[]
数组中,数组中的元素类型为 kmalloc_info_struct 结构,里边定义了对应尺寸通用内存池的相关信息。
const struct kmalloc_info_struct kmalloc_info[];
/* A table of kmalloc cache names and sizes */
extern const struct kmalloc_info_struct {
// slab cache 的名字
const char *name;
// slab cache 提供的内存块大小,单位为字节
unsigned int size;
} kmalloc_info[];
- size 用于指定该 slab cache 中所管理的通用内存块尺寸。
- name 为该通用 slab cache 的名称,名称形式为
kmalloc-内存块尺寸(单位字节)
,这一点我们可以通过cat /proc/slabinfo
命令查看。
const struct kmalloc_info_struct kmalloc_info[] __initconst = {
{NULL, 0}, {"kmalloc-96", 96},
{"kmalloc-192", 192}, {"kmalloc-8", 8},
{"kmalloc-16", 16}, {"kmalloc-32", 32},
{"kmalloc-64", 64}, {"kmalloc-128", 128},
{"kmalloc-256", 256}, {"kmalloc-512", 512},
{"kmalloc-1k", 1024}, {"kmalloc-2k", 2048},
{"kmalloc-4k", 4096}, {"kmalloc-8k", 8192},
{"kmalloc-16k", 16384}, {"kmalloc-32k", 32768},
{"kmalloc-64k", 65536}, {"kmalloc-128k", 131072},
{"kmalloc-256k", 262144}, {"kmalloc-512k", 524288},
{"kmalloc-1M", 1048576}, {"kmalloc-2M", 2097152},
{"kmalloc-4M", 4194304}, {"kmalloc-8M", 8388608},
{"kmalloc-16M", 16777216}, {"kmalloc-32M", 33554432},
{"kmalloc-64M", 67108864}
};
从 kmalloc_info[] 数组中我们可以看出,kmalloc 内存池体系理论上最大可以支持 64M 尺寸大小的通用内存池。
kmalloc_info[] 数组中的 index 有一个特点,从 index = 3 开始一直到数组的最后一个 index,这其中的每一个 index 都表示其对应的 kmalloc_info[index] 指向的通用 slab cache 尺寸,也就是说 kmalloc 内存池体系中的每个通用 slab cache 中内存块的尺寸由其所在的 kmalloc_info[] 数组 index 决定,对应内存块大小为:2^index
字节,比如:
- kmalloc_info[3] 对应的通用 slab cache 中所管理的内存块尺