当前位置:首页 > 清华大学操作系统lab2及slub实现实验报告
pde_t *pdep = &pgdir[PDX(la)];
if (!(*pdep & PTE_P)) { //如果不存在 struct Page *page;
if (!create || (page = alloc_page()) == NULL) { //判断是否建立 return NULL; } //设置参数,初始化 set_page_ref(page, 1);
uintptr_t pa = page2pa(page); memset(KADDR(pa), 0, PGSIZE);
*pdep = pa | PTE_U | PTE_W | PTE_P; }
return &((pte_t *)KADDR(PDE_ADDR(*pdep)))[PTX(la)]; //返回入口
练习3:释放某虚拟地址所在的页并取消对应的二级页表项的映射
if (*ptep & PTE_P) { //判断是否存在
struct Page *page = pte2page(*ptep);
if (page_ref_dec(page) == 0) { //判断是否需要释放 free_page(page); }
*ptep = 0; //初始化二级页表项 tlb_invalidate(pgdir, la); //更新tlb }
Challenge:
引入slub的原因:
Slab管理器过于复杂,管理结构消耗内存过大,尤其是在多处理器、多节点的情况下,另外slab管理器对Cache数没有限制,对内存对象的大小也没有限制,容易产生Cache泛滥的现象,因此引入了Slub,它尽管理部分满的Slab,忽略完全满的和完全空闲的slab,减少了管理队列;它仅建立几个通用Cache,并尽力避免建立专用Cache,大大缩减了Cache的数量。
对象分配过程:
(1) 如果处理器的热对象缓存不空,则直接从热对象缓存中分配内存对象,
即将freelist所指的对象分配出去,而后让freelist指向队列中下一个空闲对象。
static __always_inline void *slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node, void *addr){ void **object;
struct kmem_cache_cpu *c; unsigned long flags; local_irq_save(flags);
//获取本处理器的 kmem_cache_cpu 数据结构
c = get_cpu_slab(s, smp_processor_id());
if (unlikely(!c->freelist || !node_match(c, node)))
//假如当前活动 slab 没有空闲对象,或本处理器所 //在节点与指定节点不一致,则调用 __slab_alloc 函数
object = __slab_alloc(s, gfpflags, node, addr, c); else { //获得第一个空闲对象的指针,然后更新指针使其
//指向下一个空闲对象
object = c->freelist; c->freelist = object[c->offset]; stat(c, ALLOC_FASTPATH); }
local_irq_restore(flags); //是否需要将对象清0
if (unlikely((gfpflags & __GFP_ZERO) && object)) memset(object, 0, c->objsize);
return object; }
参数说明:
1) s:cache指针。
2) gfpflags:分配标志。
3) node:NUMA,从指定内存节点分配 4) addr:初始调用函数地址
每个内核对象缓冲区都是由 kmem_cache 类型的数据结构来描述的:
(2) 如果处理器的热对象缓存为空但它的当前Slab不空,则将当前Slab的第
一个空闲对象分配出去,将其空余的对象转移到处理器的热对象缓存中。转移后当前slab变成完全满的。
(3) 如果处理器的当前Slab为空但节点的partial队列不空,则从中选择一个
Slab,将它的第一个空闲对象分配出去,将其余的空闲对象全部转移到处理器的热对象缓存中。转移之后,新的Slab变成处理器当前的slab,其page结构从partial队列中摘下,page结构中的freelist被清空,inuse被设定为完全满。
(4) 如果节点的partial队列也为空,则创建一个新的Slab,将它的第一个空
闲对象分配出去,将其余空闲对象转移到处理器的热对象缓存中,使之成为当前slab。新的slab是从伙伴内存管理器中申请的一块物理内存页块。这些内存对象被串成一个链表,起始页page结构中的freelist指向表头。
在 SLUB 中,没有单独的 Empty slab 队列。每个 NUMA 节点使用 kmem_cache_node 结构维护一个处于 Partial 状态的 slab 队列。
static void *__slab_alloc(struct kmem_cache *s, gfp_t gfpflags, int node,unsigned long addr, struct kmem_cache_cpu *c)
{
void **object; struct page *new;
/* We handle __GFP_ZERO in the caller */ gfpflags &= ~__GFP_ZERO; /* 还没有local slab,从部分满slab链中提取一个slab作为local slab。Page指针保存local slab的首页 */
if (!c->page)
goto new_slab;
slab_lock(c->page);
/* local slab与指定内存节点不匹配,去激活local slab */ if (unlikely(!node_match(c, node))) goto another_slab; /* local slab重填统计 */ stat(s, ALLOC_REFILL);
/* 将local slab freelist中的空闲对象填充到cpu freelist中 */ load_freelist:
/* 检查local slab的freelist指针 */
object = c->page->freelist;
/* local slab没有空闲对象,去激活local slab */ if (unlikely(!object))
goto another_slab; if (kmem_cache_debug(s)) goto debug;
/* 将local slab freelist中的对象全部填充到cpu freelist中。这个“填充”动作实际上只是一个指针接管 */
c->freelist = get_freepointer(s, object);
/* local slab的所有空闲对象都分配给了cpu freelist(而不是一般意义上的分配给进程)*/
c->page->inuse = c->page->objects;
/* slab freelist无空闲对象了,全部分给了cpu freelist */ c->page->freelist = NULL;
/* local slab所在的内存节点 */ c->node = page_to_nid(c->page); unlock_out:
slab_unlock(c->page); stat(s, ALLOC_SLOWPATH); /* 返回空闲对象 */ return object;
another_slab:
/* 去激活旧的local slab */ deactivate_slab(s, c);
new_slab:
/* 提取一个部分满的slab。如果没有指定node,那么可能来自于本内存节点,也可能来自于其他节点。如果指定了node,那么只可能来自于本内存节点。*/
new = get_partial(s, gfpflags, node); if (new) {
/* local slab指向新的slab */ c->page = new;
stat(s, ALLOC_FROM_PARTIAL); /* 重试freelist填充流程 */ goto load_freelist; }
/* 走到这说明没有部分满的slab了,需要创建一个新slab */
gfpflags &= gfp_allowed_mask; if (gfpflags & __GFP_WAIT) local_irq_enable(); /* 创建一个新slab */
new = new_slab(s, gfpflags, node);
/*加入 gfpflags 标志置有 __GFP_WAIT,开启中断,故后续创建 slab 操作可以睡眠*/
if (gfpflags & __GFP_WAIT) local_irq_disable();
if (new) {
/* 获得local slab所在的struct kmem_cache_cpu指针 */
c = __this_cpu_ptr(s->cpu_slab); /* 更新统计 */ stat(s, ALLOC_SLAB);
/* 清除旧的local slab */ if (c->page)
flush_slab(s, c); slab_lock(new); /* 冻结新的slab,表示它已经成为某个cpu的local slab */
__SetPageSlubFrozen(new); /* 指向新的local slab */ c->page = new;
/* 重试freelist填充流程 */
goto load_freelist; }
/* 走到这,说明已经没有空闲内存,分配失败 */ if (!(gfpflags & __GFP_NOWARN) && printk_ratelimit()) slab_out_of_memory(s, gfpflags, node); return NULL; debug:
if (!alloc_debug_processing(s, c->page, object, addr)) goto another_slab;
c->page->inuse++;
c->page->freelist = get_freepointer(s, object); c->node = NUMA_NO_NODE; goto unlock_out; }
共分享92篇相关文档