简介
linux 内核内存管理算法有管理页面分配的伙伴算法,和对于小块内存的slab、slob、slub算法。其中slab是slob和slub的基础,slob多用于嵌入式设备中,目前linux内核中用到的内存管理算法是slub。本次分为下面几个章节从源码出发分析slub算法。我用的源码不是最新的是linux-5.13版本,主要是最开始看的时候用的这个版本,注释都写在这里,写博客的时候懒得换最新的了,可能有一些错误,欢迎指正。
整体目录
本系列文章从slub算法的结构体分析起,包括初始化、kmalloc申请操作、kfree释放操作和最后的销毁操作以及一些其他相关知识。
[linux kernel]slub内存管理分析(0) 导读
[linux kernel]slub内存管理分析(1) 结构体
[linux kernel]slub内存管理分析(2) 初始化
[linux kernel]slub内存管理分析(2.5) slab重用
[linux kernel]slub内存管理分析(3) kmalloc
[linux kernel]slub内存管理分析(4) 细节操作以及安全加固
[linux kernel]slub内存管理分析(5) kfree
[linux kernel]slub内存管理分析(6) 销毁slab
[linux kernel]slub内存管理分析(7) MEMCG的影响与绕过
SLUB中的结构体关系图
slub 算法中涉及的结构体不多,总共只有:
struct kmem_cache
struct kmem_cache_cpu
struct kmem_cache_node
struct page
他们之间的关系如下图:
kmalloc 申请逻辑
逻辑图
逻辑简述
其实slab 就是几级缓存机制,从cpu_slab
分配最快,能从cpu_slab
当前freelist
分配就从cpu_slab
当前freelist
分配,不能则从partial
列表中把一个slab page拿出来放到freelist
,如果还没有就从node 里找slab page,都没有再新申请slab page。
- 首先判断请求
kmalloc
分配内存的大小,大于slab可以分配的上限(通常8k)则调用kmalloc_large
进行分配。kmalloc_large
则直接根据大小调用伙伴系统分配适当的页数。
- 其他大小可以使用slab申请,则根据申请
flag
和申请大小计算出kmalloc_caches
的类型和index
,获取对应的slab管理结构体。- 如果
flag
存在ACCOUNT
相关可能要切换到计数后slab,即kmalloc-cg
相关slab(slab_pre_alloc_hook
) - 获得slab 之后获取当前cpu 的
cpu_slab
,如果当前cpu_slab
的freelist
不为空,则直接将freelist
的第一个分配返回,然后freelist
向后移动,更新tid
等。 - 如果
cpu_slab
的freelist
为空,则看cpu_slab
的partial
链表是否为空,不为空则将partial
链表第一个slab page 切换给cpu_slab
freelist
,然后分配,partial
指向下一个slab page。 - 如果以上都失败,说明
cpu_slab
无法完成分配,需要新slab,则会找到合适当前运行cpu 的内存node所属kmem_cache_node
结构,查询partial
链表是否有可用slab page有的话将这个slab page给cpu_slab
,然后也从这个node取出一些slab page补充到cpu_slab->partial
,然后从freelist
分配一个,跟上面一样。 - 如果还是没有,则调用
new_slab
申请一个全新slab page,从新slab page的freelist
分配。
- 如果
kfree 释放逻辑
逻辑图
逻辑简述
内存对象释放主要思路就是,如果是页面对象,则直接伙伴系统释放,如果是slab 对象,如果在cpu_slab中,在cpu_slab中释放,如果不是,则根据释放之前之后的状态(为空、为满、半满),进行不同的操作。
-
首先找到释放的内存对象所在的page 的page结构体。如果该page不是slab,也就是说内存对象不是通过slab分配的,而是直接分配的页(大块内存),那么直接释放页。
- 大块内存分配的时候就是从伙伴系统直接分配的页,释放的时候页通过伙伴系统释放页面(
__free_pages
)
- 大块内存分配的时候就是从伙伴系统直接分配的页,释放的时候页通过伙伴系统释放页面(
-
其他内存是通过slab分配,则通过slab释放(
slab_free
)-
memcg相关处理(
memcg_slab_free_hook
) -
获取当前
cpu_slab
,如果要释放的内存对象正好属于当前cpu_slab
(可以理解为是否是从当前cpu_slab
分配的),则快速释放- 获取
cpu_slab
的freelist
,将该内存对象插入freelist
头部,刷新cpu_slab
相关信息(do_slab_free
)
- 获取
-
如果要释放的内存对象不属于当前
cpu_slab
,(当前slab page在cpu_slab->partial
、别的cpu_slab->page
、别的cpu_slab->partial
、游离状态、node->partial
、node->full
6种情况),需要慢速释放(__slab_free
)- 先把内存对象释放到slab page的
freelist
头部,更新slab page相关统计信息 - 如果该slab page为冻结状态(说明是在
cpu_slab
中的三种情况)- 则直接结束(已经将
object
放到page->freelist
了,剩下的就不用管了)
- 则直接结束(已经将
- 如果释放前该slab page是满的(freelist为空),则说明
page
目前是游离状态(不在任何列表中)或node->full
中- 如果开启
cpu->partial
,则将该slab page放到cpu_slab->partial
中(从node->full
中移除)- 如果
cpu_slab->partial
满了,则要将当前cpu_slab->partial
中的所有slab page放到node->partial
中,然后再将新的slab page放到cpu_slab->partial
中
- 如果
- 否则放入
node->partial
中(从node->full
中移除)
- 如果开启
- 如果释放后为空,则说明目前该slab page一定处在
node->partial
列表中,因为如果在cpu_slab
或者游离状态或node->full
中不管释放完是否为空,都会在上面的步骤中处理完毕- 如果当前slab 管理的
partial
页面数量满足最小要求,则将该释放后为空的slab page释放掉(__free_pages
) - 否则不变,继续呆在
node->partial
中
- 如果当前slab 管理的
- 否则说明该
page
是本来就呆在node->partial
中的半满page
,并且释放后还是半满,则什么也不操作。
- 先把内存对象释放到slab page的
-
slab page状态转换关系图
结合kmalloc
和kfree
的逻辑,可以画出slab page的状态转换关系图: