前言
这里说的是 内核中分配小对象的一种内存分配方式 slab
呵呵 经典程度不必多说了, 内核使用的大多数数据结构 基本上是基于 slab 进行内存分配的
这里 我们来看一下 slab
如何分配对象
几个分配层级, c->free_list, c->page, c->partial, new_slab
1. 先来看一下走 new_slab 的流程
如果尝试了 c->free_list, c->page, partial 之后都没有找到可用的 page, 那么会走 new_slab 来尝试分配新的物理页面, 这里的相关代码在 slub.allocate_slab
如下是 分配物理页 部分, 最终调用了 alloc_page, alloc_pages
然后之后 初始化 page 的 slab_cache, 初始化 freelist 为物理页 的虚拟地址
更新 kmem_cache_node 对应的统计信息
然后 ___slab_alloc 是回到上面 new_slab_objects, 之后 更新 c->free_list, load_freelist
然后之后是 获取 free_list 返回, 并更新 c->free_list 为下一个空闲元素
然后是走 后面的 slab_alloc_node 统一的一部分流程
2. 再来看一下走 partial 的流程
取 partial 有几条路径, 如下这里是 取的 kmem_cache_cpu 中暂存的 partial, 然后之后 进行重试
获取当前 kmem_cache_cpu 对应的 kmem_cache_node 维护的 partial 链表中获取一个可用的 page, 然后更新为 c->page, 返回 分配的对象
如果是 没有获取到空间, 则从所有的 kmem_cache_node 中尝试分配对象, 更新 c->page
3. 再来看一下走 c->page 的流程
这里是从 c->page 获取 free_list 的流程, 拿到 free_list 之后, 获取到空闲区间 分配对象, 然后走后面的 slab_alloc_node 统一的一部分流程
根据 page 获取 free_list 的实现如下, cas 更新 page->free_list 为 null
获取的是 page 中暂存的 free_list
4. 再来看一下走 c->free_list 的流程, 这是 最 common 的场景
取得是 c->free_list, 然后之后更新 c->free_list 为 next, next 是存在 next的s->offset偏移处
如何释放对象
如果待释放的物理页和 kmem_cache_cpu 持有的物理页一致, 最 common 的场景
cas 将 待释放对象添加到 c->free_list 所处的链表, 并更新 c->free_list 为 x
如果待释放的物理页和 kmem_cache_cpu 持有的物理页不一致
cas 将 待释放对象添加到 c->free_list 所处的链表, 并更新 c->free_list 为 x
如果当前页没有对象了, 并且 partial 的数量大于 kmem_cache 中约束的最小 partial 数量, 则从 partial, full 列表中移除当前页, 并且 更新 kmem_cache_node 的相关统计, free_pages 给定的物理页
如果当前页 还有对象, 或者当前页需要保留
如果 当前物理页在释放对象之前已经占满, 则将当前 物理页添加到 partial 列表中
否则 直接返回
完