1、entryac-avail+ = objp; else /这是本地缓存空闲对象大于上限的情况,先调整本地缓存 STATS_INC_FREEMISS(cachep); cache_flusharray(cachep, ac); /不过之后还是要把该对象释放给本地缓存 再看cache_flusharray()函数:static void cache_flusharray(struct kmem_cache *cachep, struct array_cache *ac) int batchcount; struct kmem_list3 *l3; int node = numa_node_id()
2、; /本地缓存能一次转出多少个对象,这个是之前规定的 batchcount = ac-batchcount;#if DEBUG BUG_ON(!batchcount | batchcount avail);#endif /获得此缓存器的三链 l3 = cachep-nodelistsnode; spin_lock(&l3-list_lock); /看是否存在本地共享缓存 if (l3-shared) struct array_cache *shared_array = l3-shared; /本地 共享 缓存还可承载的最大数目 int max = shared_array-limit - sh
3、ared_array-avail; if (max) /最大只能为max if (batchcount max) batchcount = max; /将本地缓存前面的几个对象转入本地共享缓存中,因为前面的是最早不用的 memcpy(&(shared_array-entryshared_array-avail),entry, sizeof(void *) * batchcount); /更新本地共享缓存 shared_array-avail += batchcount; goto free_done; /没有配置本地共享缓存,只能释放对象到三链中 /注意此时的 batchcount 就是原始的
4、 batchcount,也就是说可以把达到本地缓存一次性转出 batchcount 的目标 /而上面的本地共享缓存如果使用的话,有可能达不到这个目标,因为它也有 limit /不过即便达不到,由于本地共享缓存效率比三链高,这种情况也不会在到三链来,而是直接goto free_done。 free_block(cachep, ac-entry, batchcount, node);free_done:#if STATS /.DEBUG spin_unlock(& /更新本地缓存的情况avail -= batchcount; /把后面的移动到本地缓存数组前面来 memmove(ac-entry,
5、&(ac-entrybatchcount), sizeof(void *)*ac-如果没有本地共享缓存,或者本地共享缓存达到limit了,那就把该对象回收到三链中: * Caller needs to acquire correct kmem_lists list_lock */ /释放一定数目的对象static void free_block(struct kmem_cache *cachep, void *objpp, int nr_objects, int node) int i; /逐一释放对象到三链中 for (i = 0; i list); check_spinlock_acqui
6、red_node(cachep, node); check_slabp(cachep, slabp); /将对象放到其 slab 中 slab_put_obj(cachep, slabp, objp, node); STATS_DEC_ACTIVE(cachep); /增加空闲对象计数 l3-free_objects+; /* fixup slab chains */ /如果slab中全都是空闲对象 if (slabp-inuse = 0) /如果三链中空闲对象数目超过上限,直接回收整个 slab 到内存,空闲对象数减去每个slab中对象数free_objects free_limit) fr
7、ee_objects -= cachep-num; /* No need to drop any previously held * lock here, even if we have a off-slab slab * descriptor it is guaranteed to come from * a different cache, refer to comments before * alloc_slabmgmt. */ /销毁slab对象 slab_destroy(cachep, slabp); else /到这里说明空闲对象数目还没有超过三链设置的上限 /只需将此slab添加
8、到空slab链表中 list_add(&list, &slabs_free); /* Unconditionally move a slab to the end of the * partial list on free - maximum time for the * other objects to be freed, too. /将此slab添加到部分满的链表中 list_add_tail(&slabs_partial);整个流程就是这样,回收优先级:本地缓存本地共享缓存三链。销毁slab销毁slab就是释放slab管理区和对象占用的空间,还给伙伴系统。* * slab_destroy
9、 - destroy and release all objects in a slab * cachep: cache pointer being destroyed * slabp: slab pointer being destroyed * * Destroy all the objs in a slab, and release the mem back to the system. * Before calling the slab must have been unlinked from the cache. The * cache-lock is not held/needed
10、. /销毁slab,需要释放管理对象和slab对象static void slab_destroy(struct kmem_cache *cachep, struct slab *slabp) /获得slab页面的首地址,是用第一个对象的地址colouroff(对于内置式slab,colouroff已经将slab管理者包括在内了) void *addr = slabp-s_mem - slabp-colouroff; /debug用 slab_destroy_objs(cachep, slabp); /使用SLAB_DESTROY_BY_RCU来创建的高速缓存 if (unlikely(cac
11、hep-flags & SLAB_DESTROY_BY_RCU) /rcu方式释放,暂时不做分析,主要是做并行优化 struct slab_rcu *slab_rcu; slab_rcu = (struct slab_rcu *)slabp; slab_rcu-cachep = cachep;addr = addr; /注册一个回调来延期释放slab call_rcu(&slab_rcu-head, kmem_rcu_free); /释放slab占用的页面到伙伴系统中 /如果是内置式,slab管理者和slab对象在一起,可以同时释放 kmem_freepages(cachep, addr);
12、if (OFF_SLAB(cachep) /外置式,还需释放slab管理对象 kmem_cache_free(cachep-slabp_cache, slabp);与伙伴系统交互的函数暂不再本文讨论范围之内。销毁缓存器销毁缓存器首先要保证的一点就是当前缓存器中所有的对象都是空闲的,也就是之前分配出去的对象都已经释放回来了,其主要的步骤如下:将缓存器 kmem_cache 从 cache_chain 链表中删除。将本地高速缓存,align高速缓存和本地共享缓存中的对象都回收到slab三链,并释放所有的free链表,然后判断full链表以及partial链表是否都为空,如果有一个不为空说明存在非空
13、闲slab,也就是说*还有对象未释放,此时无法销毁缓存器,必须重新将缓存器添加到 cache_chain 链表中。确定所有的对象都为空闲状态后,将缓存器涉及到的所有描述符都释放(这些描述符都是保存在通用缓存器中的,如slab管理者)。负责销毁缓存器的函数为kmem_cache_destroy():/* * kmem_cache_destroy - delete a cache the cache to destroy * Remove a &struct kmem_cache object from the slab cache. * It is expected this function
14、will be called by a module when it is * unloaded. This will remove the cache completely, and avoid a duplicate * cache being allocated each time a module is loaded and unloaded, if the * module doesnt have persistent in-kernel storage across loads and unloads. * The cache must be empty before callin
15、g this function. * The caller must guarantee that noone will allocate memory from the cache * during the kmem_cache_destroy(). /销毁一个缓存器,通常这只发生在卸载module时void kmem_cache_destroy(struct kmem_cache *cachep)cachep | in_interrupt(); /* Find the cache in the chain of caches. */ mutex_lock(&cache_chain_mute
16、x); /* * the chain is never empty, cache_cache is never destroyed /将缓存器从cache_chain的链表中摘除cachep-next); if (_cache_shrink(cachep) /释放空链表中的slab,并检查其他两个链表。在销毁缓存器前,必须先销毁其中的slab /满slab链或部分满slab链不为空 slab_error(cachep, Cant free all objects); /缓存器非空,不能销毁,重新加入到cache_chain链表中next, &cache_chain); mutex_unlock
17、(& /有关rcu SLAB_DESTROY_BY_RCU) synchronize_rcu(); /底层调用_kmem_cache_destroy()函数来实现 _kmem_cache_destroy(cachep);释放空链表的slab由下面这个函数负责:/* Called with cache_chain_mutex held to protect against cpu hotplug */释放空链表中的slabstatic int _cache_shrink(struct kmem_cache *cachep) int ret = 0, i = 0; /释放本地缓存中对象 drain
18、_cpu_caches(cachep); check_irq_on(); for_each_online_node(i) nodelistsi; if (!l3) continue; /释放空链表中的slab drain_freelist(cachep, l3, l3-free_objects); /检查满slab链表和部分满slab链表是否还有slab ret += !list_empty(&slabs_full) | ! return (ret ? 1 : 0);其中用到了这几个函数:/释放本地缓存和本地共享缓存中的对象static void drain_cpu_caches(struct
19、 kmem_cache *cachep) int node; /释放每个本地缓存中的对象,注意没有 online on_each_cpu(do_drain, cachep, 1, 1); /调用了do_drain()函数 /NUMA相关,释放每个NUMA节点的alien for_each_online_node(node) if (l3 &alien) /本版本目前是空函数,暂不支持 drain_alien_cache(cachep, l3-alien); /释放本地共享缓存中的对象 if (l3) drain_array(cachep, l3, l3-shared, 1, node);/释放
20、本地缓存中的对象static void do_drain(void *arg) struct kmem_cache *cachep = arg; struct array_cache *ac; /获得本地缓存 ac = cpu_cache_get(cachep);nodelistsnode- /释放本地缓存中的对象entry, ac-avail, node);avail = 0; * Drain an array if it contains any elements taking the l3 lock only if * necessary. Note that the l3 listlo
21、ck also protects the array_cache * if drain_array() is sed on the shared array.void drain_array(struct kmem_cache *cachep, struct kmem_list3 *l3, struct array_cache *ac, int force, int node) int tofree;ac | !ac-avail) if (ac-touched &force) touched = 0; spin_lock_irq(&avail) /计算释放对象的数目,可见这个函数还支持部分释放,取决于force的bool属性 /从 drain_cpu_caches()进入时,force=1,是要全部释放的 tofree = force ?avail : (ac-limit + 4) / 5; if (tofree tofree = (ac-avail + 1) / 2; /释放对象,从entry前面开始entry, tofree, node);avail -= tofree; /后面的对象前移entrytofree), sizeof(void *) * ac- spin_unlock_irq(&drain_array()函数是把本地缓存和本地共享缓存释放到三链中,所以会用到:
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1