自我总结——slab

内核的运行肯定是要对数据进行操作,这就包括数据的分配和释放,那有些数据呢!是内核经常要获取&释放的,如果每次都到内存中去拿再释放回内存,太浪费时间。那这样就需要一种空闲链表,这个链表是一直都在,随时可以进行获取。好吧!内核不是需要数据嘛!来空闲链表中拿一个你需要的那类数据结构,内核使完了就撒手放回到空闲链表中,而不是释放回内存。那这个空闲链表存在那些问题呢?


LKD上说最主要的就是这些链表不能全局控制。什么叫全局控制?如果能全局控制又能怎么样?Og在看到这里的时候就开始有点不适的感觉,其实就是内核不知道有这个叫空闲链表的东西,所以呢!即使内核已经告罄了,空闲链表还是空闲链表,它不会因为内核的紧张而释放自己,说白了就是没有大局观,哈!不能全局控制,且先这么看吧!接着往下走。


那内核面对这个问题该怎么办呢?于是内核大牛们提供了slab分配器来管理这些空闲链表。


(来自LKD:slab分配器的概念首先在Sun公司的SunOS5.4系统中得以实现)


怎么管理的呢?:slab层首先把不同的对象划分成高速缓存组。每个高速缓存存放不同类型的对象,LKD这句话真是让Og有些反映不过来(Og挺笨的哈!)。

其实呢!就是每个对象都有一个高速缓存去管理,因为有很多对象就有很多的高速缓存,一多就成了高速缓存组!

那这些高速缓存是怎样管理各自对应的对象(数据结构)的呢!哈哈~终于谈到链表了,上面说的类似空闲链表的东西就在高速缓存里,只不过一个高速缓存放着多条这样的链表,有全空的,有半空的,有满的,我们管这些链表叫什么呢!slab!!!现在明白为什么管这叫slab了吧!


好!我们屡一屡,首先内核要频繁的使用一些种类的对象或者数据结构,于是就把这些类型的对象或者数据结构形成了缓存,一个类型放一个高速缓存里,一个高速缓存设有好几条slab,slab里面都是这种类型的数据结构。


这样slab层概念上就有了,那么内核是怎么实现的呢?内核为高速缓存和slab虚拟了两个数据结构,高速缓存对应kmem_cache,slab当然就对应slab结构,用这两个结构来描述高速缓存和slab。


我们来看看这这两个数据结构:

先来看看高速缓存的结构。

struct kmem_cache {
    struct kmem_list3 *nodelists[MAX_NUMNODES];
}


这是简化的高速缓存结构kmem_cache,其中有一个字段是kmem_list3这个结构中放着三个链表,看代码:

struct kmem_list3 {
    struct list_head slabs_partial; /* partial list first, better asm code */
    struct list_head slabs_full;
    struct list_head slabs_free;
};


看见了吗?这是简化了的kmem_list3结构,这三个链表分别表示空的slab,半空的slab,满的slab。内核如果想要这些数据结构首先从半满的slab中拿,没有半满的就拿全空的,下面看下描述slab的结构:


struct slab {
    struct list_head list;
    unsigned long colouroff;
    void *s_mem;        /* including colour offset */
    unsigned int inuse; /* num of objs active in slab */
    kmem_bufctl_t free;
    unsigned short nodeid;
};

这个是真正的描述slab的结构,没有简化的哦~

list表示这个slab链表的头,是满的,还是部分满或者是全空的~~

colouroff slab着色的偏移量

s_mem在slab中的第一个元素,数据结构,或者对象。

inuse 这个slab中以经使用的多少对象。

free 第一个空闲的对象。

这个描述slab的结构放在哪里呢?如果slab本身空间够用就放在slab的开始位置,如果不够用就需要另外分配。

下面说下slab分配器是怎么解决最开始说的那个空闲链表不能全局控制的问题。这就涉及到一些函数了,大家做好准备哈~:

首先要考虑的是:如果发现了某些内核频繁使用的数据结构,就要为这些结构建立缓存,这就需要slab分配器来创建这样的空间来建立缓存,怎么建立?看下面的函数:


static void *kmem_getpages(struct kmem_cache *cachep, gfp_t flags, int nodeid)
{
    struct page *page;
    int nr_pages;
    int i;
    flags |= cachep->gfpflags;
    if(likely(nodeid == -1)){
        addr = (void*)__get_free_pages(flags, cache->gfporder);
        if (!addr)
        return NULL;
        page = virt_to_page(addr);
    }else{
        page = alloc_pages_node(nodeid, flags, cachep->gfporder);
        if(!page)
            return NULL;
        addr = page_address(page);
    }
    i = (1 << cachep->gfporder);
    if(cachep->flags & SLAB_RECLAIM_ACCOUNT)
        atomic_add(i, &slab_reclaim_pages);
    add_page_state(nr_slab, i_;
    while(i--){
        SetPageSlab(page);
        page++;
    }
    return addr;
}

大家可以看到__get_free_pages这个函数,这就是为高速缓存分配内存的,

如果想释放内存,就要用到kmem_freepages函数了,这个函数会调用free_page函数来完成内存的释放,当然只有在高速缓存中的slab都为空的时候才能进行内存的释放!

static void kmem_freepages(struct kmem_cache *cachep, void *addr)
{
    unsigned long i = (1 << cachep->gfporder);
    struct page *page = virt_to_page(addr);
    const unsigned long nr_freed = i;

    kmemcheck_free_shadow(page, cachep->gfporder);

    if (cachep->flags & SLAB_RECLAIM_ACCOUNT)
        sub_zone_page_state(page_zone(page),
                NR_SLAB_RECLAIMABLE, nr_freed);
    else
        sub_zone_page_state(page_zone(page),
                NR_SLAB_UNRECLAIMABLE, nr_freed);
    while (i--) {
        BUG_ON(!PageSlab(page));
        __ClearPageSlab(page);
        page++;
    }
    if (current->reclaim_state)
        current->reclaim_state->reclaimed_slab += nr_freed;
    free_pages((unsigned long)addr, cachep->gfporder);
}

看见了吗?那个最后的free_pages函数?

这就是基本的slab层的实现。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值