http://blog.chinaunix.net/uid-26859697-id-5561360.html

前面已经分析了<span style="-ms-word-wrap: break-word;">slub</span>算法的初始化、缓存区的创建、对象的分配、对象的回收,最后分析一下<span style="-ms-word-wrap: break-word;">slub</span>分配算法的<span style="-ms-word-wrap: break-word;">slab</span>销毁具体实现。

Slab销毁的入口函数为<span style="-ms-word-wrap: break-word;">kmem_cache_destroy()</span>,其实现:


1. 【file:/mm/slab_common.c】
2. void kmem_cache_destroy(struct kmem_cache s)
3. {
4.     / Destroy all the children caches if we aren't a memcg cache */
5.     kmem_cache_destroy_memcg_children(s);
6.  
7.     get_online_cpus();
8.     mutex_lock(&slab_mutex);
9.     s->refcount--;
10.     if (!s->refcount) {
11.         list_del(&s->list);
12.  
13.         if (!__kmem_cache_shutdown(s)) {
14.             memcg_unregister_cache(s);
15.             mutex_unlock(&slab_mutex);
16.             if (s->flags & SLAB_DESTROY_BY_RCU)
17.                 rcu_barrier();
18.  
19.             memcg_free_cache_params(s);
20.             kfree(s->name);
21.             kmem_cache_free(kmem_cache, s);
22.         } else {
23.             list_add(&s->list, &slab_caches);
24.             mutex_unlock(&slab_mutex);
25.             printk(KERN_ERR "kmem_cache_destroy %s: Slab cache still has objects\n",
26.                 s->name);
27.             dump_stack();
28.         }
29.     } else {
30.         mutex_unlock(&slab_mutex);
31.     }
32.     put_online_cpus();
33. }
&nbsp;

该函数中<span style="-ms-word-wrap: break-word;">kmem_cache_destroy_memcg_children()</span>删除<span style="-ms-word-wrap: break-word;">memcg</span>中相关联的子<span style="-ms-word-wrap: break-word;">cache</span>数据,而<span style="-ms-word-wrap: break-word;">get_online_cpus()</span>是对<span style="-ms-word-wrap: break-word;">cpu_online_map</span>的加锁,其与末尾的<span style="-ms-word-wrap: break-word;">put_online_cpus()</span>是配对使用的。接着的<span style="-ms-word-wrap: break-word;">mutex_lock()</span>用于获取<span style="-ms-word-wrap: break-word;">slab_mutex</span>互斥锁,该锁主要用于全局资源保护。然后对<span style="-ms-word-wrap: break-word;">kmem_cache</span>的引用计数<span style="-ms-word-wrap: break-word;">refcount</span>自减操作,如果自减后<span style="-ms-word-wrap: break-word;">if (!s-&gt;refcount)</span>为<span style="-ms-word-wrap: break-word;">true</span>,即引用计数为<span style="-ms-word-wrap: break-word;">0</span>,表示该缓冲区不存在<span style="-ms-word-wrap: break-word;">slab</span>别名挂靠的情况,那么其<span style="-ms-word-wrap: break-word;">kmem_cache</span>结构可以删除,否则表示有其他缓冲区别名挂靠,仍有依赖,那么将会解锁<span style="-ms-word-wrap: break-word;">slab_mutex</span>并<span style="-ms-word-wrap: break-word;">put_online_cpus()</span>释放<span style="-ms-word-wrap: break-word;">cpu_online_map</span>锁,然后退出。

if (!s-&gt;refcount)为<span style="-ms-word-wrap: break-word;">true</span>的分支中,先<span style="-ms-word-wrap: break-word;">list_del()</span>将该<span style="-ms-word-wrap: break-word;">slab</span>管理结构<span style="-ms-word-wrap: break-word;">kmem_cache</span>从<span style="-ms-word-wrap: break-word;">slab_caches</span>全局链表中摘除,然后<span style="-ms-word-wrap: break-word;">__kmem_cache_shutdown()</span>删除<span style="-ms-word-wrap: break-word;">kmem_cache</span>结构信息。如果<span style="-ms-word-wrap: break-word;">__kmem_cache_shutdown()</span>执行成功则将返回<span style="-ms-word-wrap: break-word;">0</span>,继而<span style="-ms-word-wrap: break-word;">if (!__kmem_cache_shutdown(s))</span>为<span style="-ms-word-wrap: break-word;">true</span>,将会通过<span style="-ms-word-wrap: break-word;">memcg_unregister_cache()</span>去注册<span style="-ms-word-wrap: break-word;">memcg</span>的<span style="-ms-word-wrap: break-word;">cache</span>,并且<span style="-ms-word-wrap: break-word;">memcg_free_cache_params()</span>释放创建时申请的<span style="-ms-word-wrap: break-word;">memcg_params</span>资源空间,而<span style="-ms-word-wrap: break-word;">kfree()</span>和<span style="-ms-word-wrap: break-word;">kmem_cache_free()</span>释放<span style="-ms-word-wrap: break-word;">slub</span>的名称空间以及<span style="-ms-word-wrap: break-word;">slab</span>空间。如果<span style="-ms-word-wrap: break-word;">__kmem_cache_shutdown()</span>执行失败,那么将会把<span style="-ms-word-wrap: break-word;">slab</span>重新挂回至<span style="-ms-word-wrap: break-word;">slab_caches</span>链表,同时记录日志信息。

由此<span style="-ms-word-wrap: break-word;">slab</span>销毁完毕。

kmem_cache_destroy()的核心函数是<span style="-ms-word-wrap: break-word;">__kmem_cache_shutdown()</span>,深入分析<span style="-ms-word-wrap: break-word;">__kmem_cache_shutdown()</span>的实现:


1. 【file:/mm/slub.c】
2. int kmem_cache_shutdown(struct kmem_cache s)
3. {
4.     int rc = kmem_cache_close(s);
5.  
6.     if (!rc) {
7.         /
8.           We do the same lock strategy around sysfs_slab_add, see
9.          
kmem_cache_create. Because this is pretty much the last
10.           operation we do and the lock will be released shortly after
11.           that in slab_common.c, we could just move sysfs_slab_remove
12.           to a later point in common code. We should do that when we
13.           have a common sysfs framework for all allocators.
14.          */
15.         mutex_unlock(&slab_mutex);
16.         sysfs_slab_remove(s);
17.         mutex_lock(&slab_mutex);
18.     }
19.  
20.     return rc;
21. }
&nbsp;

该函数主要通过<span style="-ms-word-wrap: break-word;">kmem_cache_close()</span>删除<span style="-ms-word-wrap: break-word;">slab</span>的管理数据<span style="-ms-word-wrap: break-word;">kmem_cache</span>,如果执行成功,继而进入<span style="-ms-word-wrap: break-word;">if</span>分支对<span style="-ms-word-wrap: break-word;">sysfs</span>模块的<span style="-ms-word-wrap: break-word;">slab</span>做移除操作。

具体看一下<span style="-ms-word-wrap: break-word;">kmem_cache_close()</span>的实现:


1. 【file:/mm/slub.c】
2. /
3.   Release all resources used by a slab cache.
4.  /
5. static inline int kmem_cache_close(struct kmem_cache s)
6. {
7.     int node;
8.  
9.     flush_all(s);
10.     / Attempt to free all objects /
11.     for_each_node_state(node, N_NORMAL_MEMORY) {
12.         struct kmem_cache_node *n = get_node(s, node);
13.  
14.         free_partial(s, n);
15.         if (n->nr_partial || slabs_node(s, node))
16.             return 1;
17.     }
18.     free_percpu(s->cpu_slab);
19.     free_kmem_cache_nodes(s);
20.     return 0;
21. }
&nbsp;

该函数通过<span style="-ms-word-wrap: break-word;">flush_all()</span>释放本地<span style="-ms-word-wrap: break-word;">CPU</span>的缓存区,即<span style="-ms-word-wrap: break-word;">kmem_cache_cpu</span>管理的缓存区空间;然后通过<span style="-ms-word-wrap: break-word;">for_each_node_state()</span>遍历各节点,转而<span style="-ms-word-wrap: break-word;">get_node()</span>获取节点下的<span style="-ms-word-wrap: break-word;">kmem_cache_node</span>管理结构,然后将其半满队列中的缓存区进行释放<span style="-ms-word-wrap: break-word;">free_partial()</span>;最后将<span style="-ms-word-wrap: break-word;">kmem_cache</span>的每<span style="-ms-word-wrap: break-word;">CPU</span>缓存管理<span style="-ms-word-wrap: break-word;">kmem_cache_cpu</span>通过<span style="-ms-word-wrap: break-word;">free_percpu()</span>归还给系统,同时通过<span style="-ms-word-wrap: break-word;">free_kmem_cache_nodes()</span>释放各内存节点<span style="-ms-word-wrap: break-word;">node</span>的缓存管理结构<span style="-ms-word-wrap: break-word;">kmem_cache_node</span>占用的空间释放。

最后分析一下较为复杂的<span style="-ms-word-wrap: break-word;">flush_all()</span>的实现:


1. 【file:/mm/slub.c】
2. static void flush_all(struct kmem_cache *s)
3. {
4.     on_each_cpu_cond(has_cpu_slab, flush_cpu_slab, s, 1, GFP_ATOMIC);
5. }
&nbsp;

看似封装了<span style="-ms-word-wrap: break-word;">on_each_cpu_cond()</span>函数,实际上<span style="-ms-word-wrap: break-word;">on_each_cpu_cond()</span>并不执行任何与资源释放的操作,其主要是遍历各个<span style="-ms-word-wrap: break-word;">CPU</span>,然后执行作为入参传入的函数<span style="-ms-word-wrap: break-word;">has_cpu_slab()</span>,以判断各个处理器上的资源是否存在,如果存在,继而将会通过<span style="-ms-word-wrap: break-word;">flush_cpu_slab()</span>对该处理器上的资源进行释放处理。

照例,还是详细看一下<span style="-ms-word-wrap: break-word;">on_each_cpu_cond()</span>函数实现:


1. 【file:/mm/slub.c】
2. /
3.   on_each_cpu_cond(): Call a function on each processor for which
4.   the supplied function cond_func returns true, optionally waiting
5.   for all the required CPUs to finish. This may include the local
6.   processor.
7.   @cond_func: A callback function that is passed a cpu id and
8.   the the info parameter. The function is called
9.   with preemption disabled. The function should
10.   return a blooean value indicating whether to IPI
11.   the specified CPU.
12.   @func: The function to run on all applicable CPUs.
13.   This must be fast and non-blocking.
14.   @info: An arbitrary pointer to pass to both functions.
15.   @wait: If true, wait (atomically) until function has
16.   completed on other CPUs.
17.   @gfp_flags: GFP flags to use when allocating the cpumask
18.   used internally by the function.
19.  
20.   The function might sleep if the GFP flags indicates a non
21.   atomic allocation is allowed.
22.  
23.   Preemption is disabled to protect against CPUs going offline but not online.
24.   CPUs going online during the call will not be seen or sent an IPI.
25.  
26.   You must not call this function with disabled interrupts or
27.   from a hardware interrupt handler or from a bottom half handler.
28.  /
29. void on_each_cpu_cond(bool (cond_func)(int cpu, void info),
30.             smp_call_func_t func, void info, bool wait,
31.             gfp_t gfp_flags)
32. {
33.     cpumask_var_t cpus;
34.     int cpu, ret;
35.  
36.     might_sleep_if(gfp_flags & GFP_WAIT);
37.  
38.     if (likely(zalloc_cpumask_var(&cpus, (gfp_flags|
GFP_NOWARN)))) {
39.         preempt_disable();
40.         for_each_online_cpu(cpu)
41.             if (cond_func(cpu, info))
42.                 cpumask_set_cpu(cpu, cpus);
43.         on_each_cpu_mask(cpus, func, info, wait);
44.         preempt_enable();
45.         free_cpumask_var(cpus);
46.     } else {
47.         /
48.           No free cpumask, bother. No matter, we'll
49.           just have to IPI them one by one.
50.          /
51.         preempt_disable();
52.         for_each_online_cpu(cpu)
53.             if (cond_func(cpu, info)) {
54.                 ret = smp_call_function_single(cpu, func,
55.                                 info, wait);
56.                 WARN_ON_ONCE(!ret);
57.             }
58.         preempt_enable();
59.     }
60. }
&nbsp;

该函数的入参<span style="-ms-word-wrap: break-word;">cond_func</span>是一个钩子函数,用于根据调用者传入的<span style="-ms-word-wrap: break-word;">CPU</span>信息参数来判断是否需要打断该<span style="-ms-word-wrap: break-word;">CPU</span>以执行入参<span style="-ms-word-wrap: break-word;">func</span>的操作;而入参<span style="-ms-word-wrap: break-word;">info</span>是作为<span style="-ms-word-wrap: break-word;">cond_func</span>和<span style="-ms-word-wrap: break-word;">func</span>处理函数的入参;至于入参<span style="-ms-word-wrap: break-word;">wait</span>则是一个<span style="-ms-word-wrap: break-word;">bool</span>类型,用以判断是否需要等待<span style="-ms-word-wrap: break-word;">func</span>在各<span style="-ms-word-wrap: break-word;">CPU</span>上执行完毕,如果为<span style="-ms-word-wrap: break-word;">true</span>将会等待;最后的<span style="-ms-word-wrap: break-word;">gfp_flags</span>入参是作为申请<span style="-ms-word-wrap: break-word;">cpumask</span>空间的标识。

了解完参数的意思,那么具体看一下其实现,首先<span style="-ms-word-wrap: break-word;">might_sleep_if()</span>判断是否需要休眠等待,继而通过<span style="-ms-word-wrap: break-word;">zalloc_cpumask_var()</span>申请<span style="-ms-word-wrap: break-word;">cpumask</span>的空间;申请到空间后,<span style="-ms-word-wrap: break-word;">preempt_disable()</span>禁止内核抢占后,将<span style="-ms-word-wrap: break-word;">for_each_online_cpu()</span>遍历各个<span style="-ms-word-wrap: break-word;">CPU</span>,根据<span style="-ms-word-wrap: break-word;">cond_func()</span>(即<span style="-ms-word-wrap: break-word;">has_cpu_slab()</span>)判断是否需要对该<span style="-ms-word-wrap: break-word;">CPU</span>进行打断处理,如果需要则<span style="-ms-word-wrap: break-word;">cpumask_set_cpu()</span>对该<span style="-ms-word-wrap: break-word;">CPU</span>进行标志;标志完后,根据前面的标志,通过<span style="-ms-word-wrap: break-word;">on_each_cpu_mask()</span>打断各个标志位对应的<span style="-ms-word-wrap: break-word;">CPU</span>去执行<span style="-ms-word-wrap: break-word;">func()</span>的操作(即<span style="-ms-word-wrap: break-word;">flush_cpu_slab()</span>);完了将会恢复抢占,释放<span style="-ms-word-wrap: break-word;">cpumask</span>空间。至于<span style="-ms-word-wrap: break-word;">zalloc_cpumask_var()</span>申请不到空间,将会逐个处理器进行打断再进行处理,其最终功能和作用与申请到空间的情况都是一致的,具体实现就不分析了。

相应看一下作为<span style="-ms-word-wrap: break-word;">on_each_cpu_cond()</span>入参的钩子函数<span style="-ms-word-wrap: break-word;">has_cpu_slab()</span>的实现:


1. 【file:/mm/slub.c】
2. static bool has_cpu_slab(int cpu, void info)
3. {
4.     struct kmem_cache s = info;
5.     struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
6.  
7.     return c->page || c->partial;
8. }
&nbsp;

可以看到该函数主要是用于判断本地<span style="-ms-word-wrap: break-word;">CPU</span>是否占有缓存区,如果有则返回<span style="-ms-word-wrap: break-word;">true</span>。也即意味着该<span style="-ms-word-wrap: break-word;">CPU</span>需要被打断去执行其本地的缓存区释放操作。

至于<span style="-ms-word-wrap: break-word;">on_each_cpu_cond()</span>另一钩子函数<span style="-ms-word-wrap: break-word;">flush_cpu_slab()</span>的实现:


1. 【file:/mm/slub.c】
2. static void flush_cpu_slab(void d)
3. {
4.     struct kmem_cache s = d;
5.  
6.     __flush_cpu_slab(s, smp_processor_id());
7. }
&nbsp;

该函数封装了<span style="-ms-word-wrap: break-word;">__flush_cpu_slab()</span>,实现为:


1. 【file:/mm/slub.c】
2. /
3.   Flush cpu slab.
4.  
5.   Called from IPI handler with interrupts disabled.
6.  /
7. static inline void __flush_cpu_slab(struct kmem_cache s, int cpu)
8. {
9.     struct kmem_cache_cpu *c = per_cpu_ptr(s->cpu_slab, cpu);
10.  
11.     if (likely(c)) {
12.         if (c->page)
13.             flush_slab(s, c);
14.  
15.         unfreeze_partials(s, c);
16.     }
17. }
&nbsp;

函数实现很简单,主要用于将本地<span style="-ms-word-wrap: break-word;">CPU</span>的缓存区进行释放。其首先获取本地<span style="-ms-word-wrap: break-word;">CPU</span>的<span style="-ms-word-wrap: break-word;">kmem_cache_cpu</span>管理结构,如果本地<span style="-ms-word-wrap: break-word;">CPU</span>存在缓存区的占用,将会通过<span style="-ms-word-wrap: break-word;">flush_slab()</span>去释放本地缓存区,继而通过<span style="-ms-word-wrap: break-word;">unfreeze_partials()</span>将本地<span style="-ms-word-wrap: break-word;">CPU</span>半满缓存列表进行释放。

而<span style="-ms-word-wrap: break-word;">flush_slab()</span>具体实现:


1. 【file:/mm/slub.c】
2. static inline void flush_slab(struct kmem_cache s, struct kmem_cache_cpu c)
3. {
4.     stat(s, CPUSLAB_FLUSH);
5.     deactivate_slab(s, c->page, c->freelist);
6.  
7.     c->tid = next_tid(c->tid);
8.     c->page = NULL;
9.     c->freelist = NULL;
10. }
&nbsp;

其主要是通过<span style="-ms-word-wrap: break-word;">deactivate_slab()</span>去激活本地缓存区,也即是将缓存区进行释放操作。具体<span style="-ms-word-wrap: break-word;">deactivate_slab()</span>的实现:


1. 【file:/mm/slub.c】
2. /
3.   Remove the cpu slab
4.  /
5. static void deactivate_slab(struct kmem_cache s, struct page page,
6.                 void freelist)
7. {
8.     enum slab_modes { M_NONE, M_PARTIAL, M_FULL, M_FREE };
9.     struct kmem_cache_node n = get_node(s, page_to_nid(page));
10.     int lock = 0;
11.     enum slab_modes l = M_NONE, m = M_NONE;
12.     void nextfree;
13.     int tail = DEACTIVATE_TO_HEAD;
14.     struct page new;
15.     struct page old;
16.  
17.     if (page->freelist) {
18.         stat(s, DEACTIVATE_REMOTE_FREES);
19.         tail = DEACTIVATE_TO_TAIL;
20.     }
21.  
22.     /
23.       Stage one: Free all available per cpu objects back
24.       to the page freelist while it is still frozen. Leave the
25.       last one.
26.      
27.       There is no need to take the list->lock because the page
28.       is still frozen.
29.      /
30.     while (freelist && (nextfree = get_freepointer(s, freelist))) {
31.         void prior;
32.         unsigned long counters;
33.  
34.         do {
35.             prior = page->freelist;
36.             counters = page->counters;
37.             set_freepointer(s, freelist, prior);
38.             new.counters = counters;
39.             new.inuse--;
40.             VM_BUG_ON(!new.frozen);
41.  
42.         } while (!__cmpxchg_double_slab(s, page,
43.             prior, counters,
44.             freelist, new.counters,
45.             "drain percpu freelist"));
46.  
47.         freelist = nextfree;
48.     }
49.  
50.     /
51.       Stage two: Ensure that the page is unfrozen while the
52.       list presence reflects the actual number of objects
53.       during unfreeze.
54.      
55.       We setup the list membership and then perform a cmpxchg
56.       with the count. If there is a mismatch then the page
57.       is not unfrozen but the page is on the wrong list.
58.      
59.       Then we restart the process which may have to remove
60.       the page from the list that we just put it on again
61.       because the number of objects in the slab may have
62.       changed.
63.      /
64. redo:
65.  
66.     old.freelist = page->freelist;
67.     old.counters = page->counters;
68.     VM_BUG_ON(!old.frozen);
69.  
70.     / Determine target state of the slab /
71.     new.counters = old.counters;
72.     if (freelist) {
73.         new.inuse--;
74.         set_freepointer(s, freelist, old.freelist);
75.         new.freelist = freelist;
76.     } else
77.         new.freelist = old.freelist;
78.  
79.     new.frozen = 0;
80.  
81.     if (!new.inuse && n->nr_partial > s->min_partial)
82.         m = M_FREE;
83.     else if (new.freelist) {
84.         m = M_PARTIAL;
85.         if (!lock) {
86.             lock = 1;
87.             /
88.               Taking the spinlock removes the possiblity
89.               that acquire_slab() will see a slab page that
90.               is frozen
91.              /
92.             spin_lock(&n->list_lock);
93.         }
94.     } else {
95.         m = M_FULL;
96.         if (kmem_cache_debug(s) && !lock) {
97.             lock = 1;
98.             /
99.               This also ensures that the scanning of full
100.               slabs from diagnostic functions will not see
101.               any frozen slabs.
102.              */
103.             spin_lock(&n->list_lock);
104.         }
105.     }
106.  
107.     if (l != m) {
108.  
109.         if (l == M_PARTIAL)
110.  
111.             remove_partial(n, page);
112.  
113.         else if (l == M_FULL)
114.  
115.             remove_full(s, n, page);
116.  
117.         if (m == M_PARTIAL) {
118.  
119.             add_partial(n, page, tail);
120.             stat(s, tail);
121.  
122.         } else if (m == M_FULL) {
123.  
124.             stat(s, DEACTIVATE_FULL);
125.             add_full(s, n, page);
126.  
127.         }
128.     }
129.  
130.     l = m;
131.     if (!__cmpxchg_double_slab(s, page,
132.                 old.freelist, old.counters,
133.                 new.freelist, new.counters,
134.                 "unfreezing slab"))
135.         goto redo;
136.  
137.     if (lock)
138.         spin_unlock(&n->list_lock);
139.  
140.     if (m == M_FREE) {
141.         stat(s, DEACTIVATE_EMPTY);
142.         discard_slab(s, page);
143.         stat(s, FREE_SLAB);
144.     }
145. }
&nbsp;

if (page-&gt;freelist)判断<span style="-ms-word-wrap: break-word;">slab</span>的空闲链表<span style="-ms-word-wrap: break-word;">freelist</span>是否为空,如果为空,意味着该缓存区的对象已经全部分配到了<span style="-ms-word-wrap: break-word;">CPU</span>的<span style="-ms-word-wrap: break-word;">kmem_cache_cpu</span>中<span style="-ms-word-wrap: break-word;">freelist</span>链表中;如果不为空,那么表示该<span style="-ms-word-wrap: break-word;">CPU</span>的<span style="-ms-word-wrap: break-word;">slab</span>对象被其他<span style="-ms-word-wrap: break-word;">CPU</span>释放了,将会更新统计同时设置<span style="-ms-word-wrap: break-word;">tail</span>标识为<span style="-ms-word-wrap: break-word;">DEACTIVATE_TO_TAIL</span>。

接下来的<span style="-ms-word-wrap: break-word;">while</span>循环是去激活本地<span style="-ms-word-wrap: break-word;">CPU</span>的<span style="-ms-word-wrap: break-word;">slab</span>步骤一,其主要是通过<span style="-ms-word-wrap: break-word;">while</span>循环遍历<span style="-ms-word-wrap: break-word;">CPU</span>上的<span style="-ms-word-wrap: break-word;">freelist</span>链表<span style="-ms-word-wrap: break-word;">get_freepointer()</span>获取空闲对象,继而通过内部的<span style="-ms-word-wrap: break-word;">do-while</span>循环,借用<span style="-ms-word-wrap: break-word;">__cmpxchg_double_slab()</span>比较交换将对象以插入缓存区页面的<span style="-ms-word-wrap: break-word;">freelist</span>空闲链表头的方式归还回去。<span style="-ms-word-wrap: break-word;">__cmpxchg_double_slab()</span>前面已经介绍过了的原子操作,这里将不再赘述。不过有个点值得注意的是该步骤的释放操作,其并未将所有的对象都归还回去,这是由于<span style="-ms-word-wrap: break-word;">nextfree = get_freepointer(s, freelist)</span>该步骤取下一个空闲对象时得到空指针,那么将会退出<span style="-ms-word-wrap: break-word;">while</span>循环;也就意味着如果<span style="-ms-word-wrap: break-word;">deactivate_slab()</span>入参中<span style="-ms-word-wrap: break-word;">freelist</span>不为空,那么<span style="-ms-word-wrap: break-word;">while</span>循环退出时,其也必定不为空,其具体用意稍后再分析。简而言之该步骤其目的是,当页面还处于冻结状态,将会释放每<span style="-ms-word-wrap: break-word;">CPU</span>的所有可用的对象回到缓冲区的空闲列表中。

然后是步骤二,即<span style="-ms-word-wrap: break-word;">redo</span>标签以下的动作,其首先将缓存区的<span style="-ms-word-wrap: break-word;">freelist</span>以及<span style="-ms-word-wrap: break-word;">counters</span>信息存到临时<span style="-ms-word-wrap: break-word;">old</span>结构中以备后用,接着<span style="-ms-word-wrap: break-word;">if (freelist)</span>如果为<span style="-ms-word-wrap: break-word;">true</span>,将会把前面步骤一未被归还的那个对象归还到缓冲区中,同时更新<span style="-ms-word-wrap: break-word;">new</span>信息,此时<span style="-ms-word-wrap: break-word;">new.freelist</span>持有该缓存区的所有空闲对象。往下<span style="-ms-word-wrap: break-word;">new.frozen = 0</span>将临时缓存区状态设置为非冻结;然后<span style="-ms-word-wrap: break-word;">if (!new.inuse &amp;&amp; n-&gt;nr_partial &gt; s-&gt;min_partial)</span><span style="-ms-word-wrap: break-word;"> </span>表示该<span style="-ms-word-wrap: break-word;">slab</span>缓存区中无对象被使用,且部分满<span style="-ms-word-wrap: break-word;">slab</span>个数大于最小值,意味着该缓存区需要被销毁,标识<span style="-ms-word-wrap: break-word;">m</span>为<span style="-ms-word-wrap: break-word;">M_FREE</span>;而<span style="-ms-word-wrap: break-word;">else if (new.freelist)</span>表示<span style="-ms-word-wrap: break-word;">freelist</span>不为空,仅使用了部分对象,则标识<span style="-ms-word-wrap: break-word;">m</span>为<span style="-ms-word-wrap: break-word;">M_PARTIAL</span>;至于最后的<span style="-ms-word-wrap: break-word;">else</span>分支,表示<span style="-ms-word-wrap: break-word;">freelist</span>为空,该缓存区所有对象均已被使用,<span style="-ms-word-wrap: break-word;">m</span>标识为<span style="-ms-word-wrap: break-word;">M_FULL</span>。再往下<span style="-ms-word-wrap: break-word;">if (l != m)</span>的比较是用于判断上一次的缓存区状态<span style="-ms-word-wrap: break-word;">l</span>与接下来的操作状态<span style="-ms-word-wrap: break-word;">m</span>是否一致,不一致则意味着需要发生变更,其将会先判断<span style="-ms-word-wrap: break-word;">l</span>的状态为<span style="-ms-word-wrap: break-word;">M_PARTIAL</span>或<span style="-ms-word-wrap: break-word;">M_FULL</span>,继而采取对应的<span style="-ms-word-wrap: break-word;">remove_partial()</span>或<span style="-ms-word-wrap: break-word;">remove_full()</span>链表摘除操作;继而根据<span style="-ms-word-wrap: break-word;">m</span>的状态,往半满链表中添加<span style="-ms-word-wrap: break-word;">add_partial()</span>还是往满载链表中添加<span style="-ms-word-wrap: break-word;">add_full()</span>,接着将<span style="-ms-word-wrap: break-word;">l</span>的状态更新为<span style="-ms-word-wrap: break-word;">m</span>。现在到了<span style="-ms-word-wrap: break-word;">if (!__cmpxchg_double_slab())</span>,这里是用于判断自<span style="-ms-word-wrap: break-word;">redo</span>到此,缓存区是否发生过对象操作变更,如果没发生过的话,将会把<span style="-ms-word-wrap: break-word;">new</span>暂存的空闲对象挂载到缓存区中以及更新<span style="-ms-word-wrap: break-word;">counters</span>,否则将会跳转回<span style="-ms-word-wrap: break-word;">redo</span>标签重新执行前面的操作。至此,顺利的话,缓存区已经去激活完毕了。

最后如果<span style="-ms-word-wrap: break-word;">m</span>的状态为<span style="-ms-word-wrap: break-word;">M_FREE</span>,则表示该缓存区不需要再使用了,将通过<span style="-ms-word-wrap: break-word;">discard_slab()</span>将其销毁。

至此,<span style="-ms-word-wrap: break-word;">slub</span>算法分析完毕。