1. References
- https://zhuanlan.zhihu.com/p/701213723 (图解Linux内存管理_slab,slub,slob分配器)
- Linux之slub分配器分析_linux slab-CSDN博客 (Linux之slub分配器分析)
- SLUB DEBUG分析内存泄漏_slabinfo-CSDN博客 (SLUB DEBUG分析内存泄漏)
2. Linux slab分配器
Linux伙伴分配器在分配内存时是以物理页面为单位的,在实际中有很多内存需求是以字节为单位的,那么如果我们需要分配以字节为单位的小内存块,该如何分配呢?Linux slab分配器就是用来解决小内存块分配问题的,也是内存分配中非常重要的角色之一。
Linux slab分配器最终还使用Linux伙伴分配器来分配实际的物理页面,只不过slab分配器在这些连续的物理页面上实现了自己的机制,以此来对小内存块进行管理。
2.1 Linux slab分配器产生的背景
内核常常需要分配几十字节的小内存块,若为其分配一个物理页面,则非常浪费内存。早期Linux内核实现了以2n字节为大小的内存块分配机制,这个机制非常类似于伙伴系统。这个简单的机制虽然减少了内存浪费, 但是并不高效。
一个更好的机制是Sun公司发明的slab机制,最早实现在Solaris 2.4操作系统中。slab分配器是一种基于slab缓存的内存分配器。它将Linux内核中常用的数据结构和对象(所谓的对象(object)就是内核中的数据结构(例如:task_struct,file_struct 等))分成若干个大小相等的slab缓存,每个slab缓存对应一种大小的数据结构或对象。当需要分配内存时,就从相应大小的slab缓存中获取一块空闲内存,当不需要使用时,就将其释放到相应大小的slab缓存中。 如下是i.MX8MP Linux 5.15.x的slab信息:
每一列所代表的含义为:
- name:slab object(对象)的名称。
- active_objs:活动object个数,即被申请走的object个数(正在被使用)。
- num_objs:总的object个数,slab本身具备缓存作用,所以num_objs可能会比active_objs要大,这是一种合理的现象。
- objsize:每个object大小,以字节为单位。
- objperslab:slab中存放的是object,这个指标表示一个slab中包含多少个object。
- pagesperslab: 一个slab占用几个page。
- tunables:
- limit:
- batchcount:
- sharedfactor:
- slabdata:
- active_slabs:活动slab个数。
- num_slabs:总slab个数。
- sharedavail:
为了方便使用,块分配器在初始化的时候创建了一些通用的内存缓存,对象的长度大多数是 2n字节,从普通区域分配页的内存缓存的名称是"kmalloc-<size>"(size 是对象的长度),从DMA区域分配页的内存缓存的名称是"dma-kmalloc-<size>"。
使用通用的内存缓存的缺点是:块分配器需要找到一个对象的长度刚好大于或等于请求的内存长度的通用内存缓存,如果请求的内存长度和内存缓存的对象长度相差很远,浪费比较大,例如申请36字节,实际分配的内存长度是64字节,浪费了28字节。所以有时候使用者需要创建专用的内存缓存,编程接口如下:
2.1.1 Linux slab分配器的核心思想
为每种对象类型创建一个内存缓存,每个内存缓存由多个大块(slab,原意是大块的混凝土)组成,一个大块是一个或多个连续的物理页,每个大块包含多个对象。slab采用了面向对象的思想,基于对象类型管理内存,每种对象被划分为一类。例如,进程描述符(task_struct)是一个类,每个进程描述符实例是一个对象。Linux slab分配器的主要结构体:
Linux slab分配器在某些情况下表现不太好,所以Linux内核提供了两个改进的块分配器:
- 在配备了大量物理内存的大型计算机上,slab分配器的管理数据结构的内存开销比较大,所以设计了slub分配器。
- 在小内存的嵌入式设备上,slab分配器的代码太多、太复杂,所以设计了一个精简的slob分配器。slob是"Simple List Of Blocks"的缩写,意思是简单的块链表。
2.2 i.MX8MP示例
2.2.1 查看slab信息
在i.MX8MP Linux 5.15.x中执行命令:cat /proc/meminfo,可以查看总的slab内存池内存使用情况。
slab(总内存)= SReclaimalbe(可回收内存) + SUnreclaim(不可回收内存)