这篇slub算法对具体实现讲得很生动。图画的是真不错,我就偷懒不画这么好看了。大家自行前往参考。
此链接中描述的,我就不再多讲了。我来讲点上面没有的。
slub的运行最关键的两个函数是___slab_alloc()和__slab_free()。如果能看懂这两个,那基本上就是对slub了解了。一下的讲述都基于个人理解,若有偏差,欢迎指正。
你的样子
slab的原意是:厚板,大块。也就是我们从内存中,预先挖下一块,按照我们的要求安放好。等到要用的时候可以拿出来用。在上面给大家指出的这个链接上,作者的比喻更生动。slub像是零售商,从伙伴系统上批发内存,再零售出去。
更有意思的是,零售商有营业厅和仓库。营业厅只保留一个slab,当这个slab用完的时候,才从仓库里取出一个,并把这个营业厅里的下架。
好了,我唠叨了半天估计你也烦了。那我想说的是什么呢?我想说的是,一直在说slab/slub,那它究竟长什么样子呢?也就是我们所说的这一块是什么呢?其实他就是page结构体。
如果说kmem_cache结构体是一个零售商,那page结构体就是真的要上架下架的那个slab。
内核中很巧妙的复用了page结构体来保存slab所需要的信息。
struct page
+------------------------------+
|slab_cache |
| (struct kmem_cache *) |
+------------------------------+
|freelist | first free object (list head)
| (viod *) |
+------------------------------+
|objects | number of objects in Page
|inuse | number of objects used in cpu_slab
|frozen | frozen means in cpu_slab
| (unsigned ) |
+------------------------------+
如果觉得烧脑,我们可以先只看前两个部分: slab_cache, freelist。
- slab_cache比较好理解,就是你这商品到底是哪个零售商的。
- freelist是一个链表头,指向了还剩下可用的内存。
因为每一次上架不止上一个,而是上架一大块。所以我们需要有个链表来指示还剩哪些。那为啥不用数组呢?因为如果是数组,我们就要用bitmap来标示用了哪些,还剩哪些。每次分配也需要搜索,每次归还还需要写回。这个操作是费时的,且容易引入竞争。
而内核的实现非常巧妙的使用了单链表。分配的时候直接取,归还的时候插入链表头部。操作简单,O(1)的复杂度。通过cmpxchg还做到了无锁化。另外,因为是插入链表头部,下次要用还会被优先取到,相对插入别的地方更不容易发生cache miss。
Brilliant Design
货架的样子
我们沿用零售商的比喻。现在便利店已经是满大街了。同样一种商品可以在不同的地区的便利店内买到。甚至我在张江门店买的梦龙和在明光村门店买的梦龙是一模一样的。那这是怎么做到的呢?每个店都上架一套呗。
内核中为了满足每个cpu上的需求,在每个cpu上都上架了一套。你可以理解为在每个cpu上都开了