专栏首页Linux内核深入分析SLUB的引入及举例说明

SLUB的引入及举例说明

我们都知道Buddy分配器是按照页的单位分配的(Buddy系统分配器实现),如果我们需要分配几十个字节,几百个字节的时候,就需要用到SLAB分配器。

SLAB分配器专门是针对小内存分配而设计的,比如我们驱动中常见的Kmalloc分配器就是通过SLAB分配器分配的内存。

而SLAB分配器在linux系统中三种具体的实现:SLAB,SLUB,SLOB。目前内核代码中默认的SLAB分配器为SLUB算法。至于为啥不用SLAB大家可以网上看看资料,所以我们重点分析SLUB分配器的实现。

SLUB的原理是:

  • SLUB的内存是从Buddy拿来的,是按照页的单位从Buddy拿过来。
  • SLUB分配器会对从Buddy拿来的内存做二次分配,分配成每个小块,叫做object
  • 每个SLUB都有一个名字,比如Kmalloc-128,当我们通过Kmalloc申请内存100字节的时候,就会去Kmalloc-128的slab里面去申请
  • 当我们内存使用完毕后,需要将申请的100字节内存还给Kmalloc-128的slab的。
  • 所以当我们再次申请同样的大小内存的时候就去对应的SLAB缓冲池去申请就行,可以提供效率。

cat /proc/slabinfo节点就可以看见系统中存在的slab信息

kmalloc-8192         274    304   8192    4    8 : tunables    0    0    0 : slabdata     76     76      0
kmalloc-4096         546    568   4096    8    8 : tunables    0    0    0 : slabdata     71     71      0
kmalloc-2048        1292   1360   2048   16    8 : tunables    0    0    0 : slabdata     85     85      0
kmalloc-1024        2134   2336   1024   32    8 : tunables    0    0    0 : slabdata     73     73      0
kmalloc-512         3648   3648    512   32    4 : tunables    0    0    0 : slabdata    114    114      0
kmalloc-256         2000   2784    256   32    2 : tunables    0    0    0 : slabdata     87     87      0
kmalloc-192         4894   5124    192   21    1 : tunables    0    0    0 : slabdata    244    244      0
kmalloc-128         3360   3360    128   32    1 : tunables    0    0    0 : slabdata    105    105      0
kmalloc-96         24906  24906     96   42    1 : tunables    0    0    0 : slabdata    593    593      0
kmalloc-64         84800  84800     64   64    1 : tunables    0    0    0 : slabdata   1325   1325      0
kmalloc-32         19456  19456     32  128    1 : tunables    0    0    0 : slabdata    152    152      0
kmalloc-16         12544  12544     16  256    1 : tunables    0    0    0 : slabdata     49     49      0
kmalloc-8          11264  11264      8  512    1 : tunables    0    0    0 : slabdata     22     22      0
kmem_cache_node      576    576     64   64    1 : tunables    0    0    0 : slabdata      9      9      0
kmem_cache           294    294    384   21    2 : tunables    0    0    0 : slabdata     14     14      0

我们通过自己创建一个新的slab,带大家走进SLUB。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mm.h>
 
static struct kmem_cache* slub_test;
 
struct student{
    int age;
    int score;
};
 
int slub_test_create_kmem(void)
{
    int ret = -1;
    slub_test = kmem_cache_create("slub_test", sizeof(struct student), 0, 0, NULL);
    if(slub_test != NULL){
        printk("slub_test create success!\n");
        ret=0;
    }
 
    return ret;
}
 
static int __init slub_test_init(void)
{
    int ret;
    printk("slub_test kernel module init\n");
    ret = slub_test_create_kmem();
    return 0;
}
 
static void __exit slub_test_exit(void)
{
    printk("slub_test kernel module exit\n");
    kmem_cache_destroy(slub_test);
}
 
module_init(slub_test_init);
module_exit(slub_test_exit);

此例子申请了一个名为slub_test的slub,我们编译为模块了。当我们插入此模块的时候,就会在/proc/slabinfo下生成一个slub_test的名字的slab

/ # cat /proc/slabinfo | grep "slub_test"
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
slub_test              0            0          8          512           1        : tunables    0          0            0        : slabdata      0               0          0

这个是插入模块后,出来的结果。而上面这些都是什么意思呢?接着分析各个字段啥意思,我们不按照顺序解释

  • name: slub_test代表我们这个slab的名字叫slub_test
  • pagesperslab: 意思是每一个slab需要几个page,可以看到名为slub_test的slab需要一个page,也就是4K。其实就是从buddy拿了一个order为0的一个页
  • objsize: 代表的是每一个object的大小,我们传入的结构体sizeof(struct student)的大小就为8
  • objperslab: 代表的一个就是一个slab中有多少个object,通过计算是512个。

注意:如果机器开启的SLUB_DEBUG选项,有可能都不是整除的,因为一个object中存在了一些debug调试区域

接着我们从我们创建的slab中申请一个object,目前我们名字slub_test的slab中是没有Object的。当通过kmem_cache_alloc去申请一个object的时候,就会从buddy去那一页,然后计算好各个各个object之间的联系,取出一个object给我们使用。上面kmem_alloc_create只是创建了一个slab,里面还没有真正的分配object的。

#include <linux/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/mm.h>
#include <linux/gfp.h>
 
static struct kmem_cache* slub_test;
 
struct student{
    int age;
    int score;
};
 
struct student* zhangsan;
 
int slub_test_create_kmem(void)
{
    int ret = -1;
    slub_test = kmem_cache_create("slub_test", sizeof(struct student), 0, 0, NULL);
    if(slub_test != NULL){
        printk("slub_test create success!\n");
        ret=0;
    }
 
    zhangsan = kmem_cache_alloc(slub_test, GFP_KERNEL);
    if(zhangsan != NULL){
        printk("alloc object success!\n");
        ret = 0;
    }
    return ret;
}
 
static int __init slub_test_init(void)
{
    int ret;
    printk("slub_test kernel module init\n");
    ret = slub_test_create_kmem();
    return 0;
}
 
static void __exit slub_test_exit(void)
{
    printk("slub_test kernel module exit\n");
    kmem_cache_free(slub_test,zhangsan);
    kmem_cache_destroy(slub_test);
}
 
module_init(slub_test_init);
module_exit(slub_test_exit);

这时候再次看看/proc/slabinfo下的结果

/proc/slabinfo | grep "slub_test"
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
slub_test            512            512        8            512         1        : tunables     0         0           0          : slabdata      1             1          0

可以看到数值有所变化:

  • num_objs: 就是代表objects的最大个数
  • active_objs:从代码中得出结果,如下
for_each_kmem_cache_node(s, node, n) {
    nr_slabs += node_nr_slabs(n);
    nr_objs += node_nr_objs(n);
    nr_free += count_partial(n, count_free);
}
 
sinfo->active_objs = nr_objs - nr_free;
sinfo->num_objs = nr_objs;
sinfo->active_slabs = nr_slabs;
sinfo->num_slabs = nr_slabs;
sinfo->objects_per_slab = oo_objects(s->oo);
sinfo->cache_order = oo_order(s->oo);

active_objects等于nr_objs - nr_free,nr_free的值是通过count_free计算出来的。free代表的意思是总共的object减去使用的inuse的。但是slab刚创建的时候insue等于object的

#ifdef CONFIG_SLUB_DEBUG
static int count_free(struct page *page)
{
    return page->objects - page->inuse;
}
 
static inline unsigned long node_nr_objs(struct kmem_cache_node *n)
{
    return atomic_long_read(&n->total_objects);
}
#endif /* CONFIG_SLUB_DEBUG */
 
 
page->inuse = page->objects;
page->frozen = 1;

这里的意思大概是inuse代表已经用完的,那slab肯定会使用完的,所以一开始inuse就等于object的数量的。

原理就是上图的样子:slab从buddy拿到一个order为0的内存,也就是一页,然后把这一页称为名为slub_test的slab,然后把这一页分成很多小的object的。

当我们使用的时候就从slab中获取一个object使用,用完了在归还给slab管理即可。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • NULL指针的奇妙之旅

    今天带大家了解下NULL指针是如何形成的? 当然了我们要深入到操作系统中去看看为何访问一个NULL指令会报Segment Fault的错误。

    DragonKingZhu
  • Tasklet

    有的时候在驱动程序中需要延迟某些操作的进行,最典型的操作就是在驱动程序的中断处理函数延迟操作。比如在DMA驱动中,当数据传输完毕之后会触发中断的,通常这时候会启...

    DragonKingZhu
  • Linux设备驱动模型-Ktype

    在之前创建的object的时候,使用的是kobject_create_and_add函数。而此函数中创建的object使用的是默认的ktype(dynamic_...

    DragonKingZhu
  • Android+struts2+json方式模拟手机登录功能

    涉及到的知识点:  1.Struts2框架的搭建(包括Struts2的jSON插件)   2.Android前台访问Web采用HttpClient方式。 ...

    xiangzhihong
  • 张正友相机标定Opencv实现以及标定流程&&标定结果评价&&图像矫正流程解析(附标定程序和棋盘图)

    使用Opencv实现张正友法相机标定之前,有几个问题事先要确认一下,那就是相机为什么需要标定,标定需要的输入和输出分别是哪些?

    用户1148525
  • python | pandas 改变列的位置、填充缺失值

    努力在北京混出人样
  • 一个完整的Django入门指南(二)

    第三部分 Introduction        In this tutorial, we are going to dive deep into two f...

    zhang_derek
  • python | pandas 改变列的位置、填充缺失值

    努力在北京混出人样
  • Python-EEG工具库MNE中文教程(8)-参考电极简介

    安置在头皮上的电极为作用电极(active electrode)。放置在身体相对零电位点的电极即为参考电极(reference electrode),也称为参考...

    脑机接口社区
  • 升级 hexo 解决 Upgrade braces to version 2.3.1

    iOSDevLog

扫码关注云+社区

领取腾讯云代金券