Linux内存管理之slab机制(分配对象)(3)

2)如果没有shared local cache,或是其中没有空闲的对象,从slab链表中分配,其中,从slab中分配时,先查看部分空余链表,然后再查看空余链表。将slab链表中的数据先放到本地CPU cache中。

3) 如果本地CPU cache中任然没有数据,那么只有重新创建一个slab,然后再试。

/*从slab三链中提取一部分空闲对象填充到local cache中*/   static void *cache_alloc_refill(struct kmem_cache *cachep, gfp_t flags)   {       int batchcount;       struct kmem_list3 *l3;       struct array_cache *ac;       int node;      retry:       check_irq_off();        /* 获得本内存节点,UMA只有一个节点 */       node = numa_node_id();        /* 获得本CPU的local cache */       ac = cpu_cache_get(cachep);       /* 批量填充的数目,local cache是按批填充的 */       batchcount = ac->batchcount;       if (!ac->touched && batchcount > BATCHREFILL_LIMIT) {           /*           * If there was little recent activity on this cache, then           * perform only a partial refill.  Otherwise we could generate           * refill bouncing.           */            /* 最近未使用过此local cache,没有必要添加过多的对象           ,添加的数目为默认的限定值 */           batchcount = BATCHREFILL_LIMIT;       }       /* 获得本内存节点、本cache的slab三链 */       l3 = cachep->nodelists[node];          BUG_ON(ac->avail > 0 || !l3);       spin_lock(&l3->list_lock);          /* See if we can refill from the shared array */       /* shared local cache用于多核系统中,为所有cpu共享      ,如果slab cache包含一个这样的结构      ,那么首先从shared local cache中批量搬运空闲对象到local cache中      。通过shared local cache使填充工作变得简单。*/       if (l3->shared && transfer_objects(ac, l3->shared, batchcount))           goto alloc_done;          /* 如果没有shared local cache,或是其中没有空闲的对象      ,从slab链表中分配 */       while (batchcount > 0) {           struct list_head *entry;           struct slab *slabp;           /* Get slab alloc is to come from. */                      /* 先从部分满slab链表中分配 */           entry = l3->slabs_partial.next;           /* next指向头节点本身,说明部分满slab链表为空 */           if (entry == &l3->slabs_partial) {               /* 表示刚刚访问了slab空链表 */               l3->free_touched = 1;               /* 检查空slab链表 */               entry = l3->slabs_free.next;               /* 空slab链表也为空,必须增加slab了 */               if (entry == &l3->slabs_free)                   goto must_grow;           }           /* 获得链表节点所在的slab */           slabp = list_entry(entry, struct slab, list);           /*调试用*/           check_slabp(cachep, slabp);           check_spinlock_acquired(cachep);              /*           * The slab was either on partial or free list so           * there must be at least one object available for           * allocation.           */           BUG_ON(slabp->inuse >= cachep->num);              while (slabp->inuse < cachep->num && batchcount--) {               /* 更新调试用的计数器 */               STATS_INC_ALLOCED(cachep);               STATS_INC_ACTIVE(cachep);               STATS_SET_HIGH(cachep);               /* 从slab中提取一个空闲对象,将其虚拟地址插入到local cache中 */               ac->entry[ac->avail++] = slab_get_obj(cachep, slabp,                                   node);           }           check_slabp(cachep, slabp);              /* move slabp to correct slabp list: */           /* 从原链表中删除此slab节点,list表示此          slab位于哪个链表(满、部分满、空)中 */           list_del(&slabp->list);           /*因为从中删除了一个slab,需要从新检查*/           if (slabp->free == BUFCTL_END)               /* 此slab中已经没有空闲对象,添加到“full”slab链表中 */               list_add(&slabp->list, &l3->slabs_full);           else               /* 还有空闲对象,添加到“partial”slab链表中 */               list_add(&slabp->list, &l3->slabs_partial);       }      must_grow:       /* 前面从slab链表中添加avail个空闲对象到local cache中      ,更新slab链表的空闲对象数 */       l3->free_objects -= ac->avail;   alloc_done:       spin_unlock(&l3->list_lock);       /* local cache中仍没有可用的空闲对象,说明slab      三链中也没有空闲对象,需要创建新的空slab了 */       if (unlikely(!ac->avail)) {           int x;           /* 创建一个空slab */           x = cache_grow(cachep, flags | GFP_THISNODE, node, NULL);              /* cache_grow can reenable interrupts, then ac could change. */           /* 上面的操作使能了中断,此期间local cache指针可能发生了变化,需要重新获得 */           ac = cpu_cache_get(cachep);           /* 无法新增空slab,local cache中也没有空闲对象,表明系统已经无法分配新的空闲对象了 */           if (!x && ac->avail == 0)    /* no objects in sight? abort */               return NULL;           /* 走到这有两种可能,第一种是无论新增空slab成功或失败,只要avail不为0          ,表明是其他进程重填了local cache,本进程就不需要重填了          ,不执行retry流程。第二种是avail为0,并且新增空slab成功          ,则进入retry流程,利用新分配的空slab填充local cache */           if (!ac->avail)      /* objects refilled by interrupt? */               goto retry;       }       /* 重填了local cache,设置近期访问标志 */       ac->touched = 1;       /* 返回local cache中最后一个空闲对象的虚拟地址 */       return ac->entry[--ac->avail];   }  

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/22824.html