这篇笔记算是对slab
的总结吧,我会边写边回顾之前的几篇笔记。有新的想法就添加,有不对的地方就修改。争取把slab
的一系列流程搞清楚。我还是先从第一个场景,就是从零开始创建普通高速缓存写起。
创建普通高速缓存
创建第一个普通高速缓存
这要从10. 【slab】2 - slab的基本操作 - kmem_cache_init()说起,在kmem_cache_init()
中,内核创建的第一个高速缓存就是普通高速缓存,我这里暂且给它取个名字 - GC1
1 | sizes->cs_cachep = kmem_cache_create( |
我们来看看,这个时候有什么,这个时候我们只有一个静态初始化的高速缓存cache_cache
,它的本地高速缓存也是静态初始化的,shared的高速缓存是空的,没有一个分配好的slab
,当然也就没有任何对象。这种情况很特殊,因此我把它放在第一个位置。我们来看看在这种情况下如何创建高速缓存。
这里我稍微详细点记录第一个高速缓存是如何创建的。
- 计算GC1中的对齐(用于对象地址、offset等地方)以及将对象的大小按
WORD
进行对齐。
GC1高速缓存的对象大小、对齐已经确定
- 获取GC1高速缓存的描述符。
kmem_cache_create()
通过调用kmem_cache_alloc() --> __cache_alloc()
从cache_cache
获取GC1缓存的描述符(cache_cache
就是专门用于这项工作);__cache_alloc()
检查其本地高速缓存是否有空闲空间 - 目前ac->avail == 0
,不满足条件。开始尝试对cache_cache
的本地高速缓存进行填充;- 查看shared的本地高速缓存是否有空闲对象 - 目前shared的本地高速缓存还为空;
- 查看是否有
slab
在free或者partial的slab
链表上 - 目前还没有任何slab
; - 需要调用
cache_grow()
来创建一个新的slab
(包括对对象的初始化)- 这个slab
是cache_cache
的,因此使用cache_cache
初始化的设置来创建这个slab
(比如对象大小,偏移,着色,slab
描述符是否在slab
外等); - 将新创建的
slab
放回slab
的free链表,然后跳到2.4,通过free链表找到这个刚创建的slab
; - 将一定数量(
< batchcount
)的对象给本地高速缓存(其实就是将对象的地址复制给本地高速缓存),在这里,cache_cache
的batchcount
和本地高速缓存的大小都是1,因此,只需要复制1个对象的地址即可; - 在
__cache_alloc()
中返回这个对象的地址,这个地址就是我们需要创建的GC1高速缓存的描述符地址。
GC1高速缓存的描述符已获取,
cache_cache
有了第一个slab
,其array_cache
的本地高速缓存实现了填充和使用
- 根据对象的大小、对齐等,计算出GC1中
slab
对象的数量、高速缓存的gfporder
、offset、着色、对象大小等等。
GC1高速缓存的
gfporder
、offset、着色、对象大小等等已经获取,其slab
的内部结构已经确定
- 创建GC1高速缓存的本地高速缓存
- 根据
g_cpucache_up
的值(NONE),使用静态分配的方法给GC1分配本地高速缓存,完成后g_cpucache_up == PARTIAL
; - 初始化GC1的本地高速缓存以及部分GC1的参数。
- 根据
GC1有了本地高速缓存,但没有空闲对象,GC1目前也没有
slab
。
- 将GC1连入
cache_chain
。
至此,GC1就算是创建成功了,但它目前还有几个缺点:
- 没有
slab
和对象; - 没有shared的高速缓存;
- 本地高速缓存还是空的;
这些缺点在之后的初始化过程中都会被修复,我到时候再说明。
创建第二个普通高速缓存
这里我就给它取名为GC2吧,我们来看看它的创建和第一个普通高速缓存的创建有什么不同。
- 这步一样。
- 这步基本一样,
cache_cache
的本地高速缓存依然没有空闲对象,去partialslab
链表找到slab
和对象,复制给cache_cache
的本地高速缓存并被使用。 - 这步一样。
- 现在
g_cpucache_up == PARTIAL
,那么就需要使用kmallo()
来获取GC2的本地高速缓存了。和cache_cache
一样,GC2的本地高速缓存也只能容纳一个对象。这个函数最终会调用__cache_alloc()
,那么就要走GC1创建过程中cache_cache
走的老路了。GC1创建一个新的slab
再获得GC2的高速缓存描述符。其它和GC1的创建一样。
GC1有了第一个
slab
,其array_cache
的本地高速缓存实现了填充和使用
- 将GC2连入
cache_chain
。
创建第三个普通高速缓存
1,2,3,5步都一样,4中,kmalloc()
去GC1的slab
中获取一个对象,不用再创建新的slab
。其它一样。
完成kmem_cache_init()剩下部分
在上一部分,我们讨论的是创建普通高速缓存,它们是在kmem_cache_init()
中完成的。到目前为止,我们可以知道,cache_cache
和GC1是真正分配了slab
,其它普通高速缓存都没有分配slab
。并且它们的本地高速缓存和只能容纳一个对象、shared高速缓存还没有初始化,这部分都在kmem_cache_init()
的后半部分完成。
在完成所有普通高速缓存的创建后,第一步就是替换掉cache_cache
和GC1中的本地高速缓存。从上面分析可以知道,这两个高速缓存的本地高速缓存都是静态分配的,我们这里要用kmalloc()
来动态的分配内存空间(从GC1中获取)。
再次总结,高速缓存的描述符从cache_cache
中获取(包括专用和普通),cache_cache
和普通高速缓存的本地高速缓存从GC1获取。
到这里,我们就需要重新初始化cache_cache
和所有普通高速缓存的本地高速缓存和shared的高速缓存了。
重新初始化本地高速缓存和shared的高速缓存
这部分直接参考11. 【Slab】3 - Slab的基本操作 - kmem_cache_create()即可,说的很清楚了(当然,我现在又回去修改了一些地方)。
这步结束后,cache_cache
, GC1, GC2, …, GCx就都完成了最后的分配,以后就可以直接使用kmalloc()
等函数了。
kmem_cache_init()结束之后创建专用高速缓存
在初始化之后,创建专用高速缓存就比较简单了。
- 这步一样。
- 这步一样,从
cache_cache
获取高速缓存描述符。 - 这步一样。
- 现在
g_cpucache_up == FULL
,说明slab
系统初始化已经完成。可以直接在这里创建本地高速缓存和shared的高速缓存。 - 这步一样。
到这里,我们就完成了对高速缓存创建的几种特殊情况的分析,同时,我们还复习了如何分配对象以及在不同的情况下,对象分配过程中的不同点。对于对象、slab
以及高速缓存的释放、销毁,我在上一篇笔记中已经详细记载了,这里就不重复了。
至此,slab
系统的学习总结要告一段落了,以后有什么新的想法再来补充、修改。