前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Linux slab分配器

Linux slab分配器

作者头像
刘盼
发布2021-04-13 14:26:39
2.1K0
发布2021-04-13 14:26:39
举报
文章被收录于专栏:人人都是极客

在Linux中,伙伴系统是以页为单位分配内存。但是现实中很多时候却以字节为单位,不然申请10Bytes内存还要给1页的话就太浪费了。slab分配器就是为小内存分配而生的。slab分配器分配内存以Byte为单位。但是slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配。

走进slub

做个小实验:

代码语言:javascript
复制
#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;
};

static void mystruct_constructor(void *addr)
{
    memset(addr, 0, sizeof(struct student));
}

struct student* peter;

int slub_test_create_kmem(void)
{
    int ret = -1;
    slub_test = kmem_cache_create("slub_test", sizeof(struct student), 0, 0, mystruct_constructor);
    if(slub_test != NULL){
        printk("slub_test create success!\n");
        ret=0;
    }


    peter = kmem_cache_alloc(slub_test, GFP_KERNEL);
    if(peter != 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_destroy(slub_test);
}

module_init(slub_test_init);
module_exit(slub_test_exit);

看下结果:

相关参数也可以通过如下路径查看:

总结:slab从buddy拿到一个order为0的内存,也就是一页,然后把这一页称为名为slub_test的slab,然后把这一页分成很多小的object的。当我们使用的时候就从slab中获取一个object使用,用完了在归还给slab管理即可。

数据结构

  • struct kmem_cache:用于管理slab缓存,包括该缓存中对象的信息描述,per-CPU/Node管理slab页面等;
  • struct kmem_cache_cpu:是对本地内存缓存池的描述,每一个cpu对应一个结构体。可以使用无锁访问,提高缓存对象分配速度;
  • struct kmem_cache_node:是对共享内存缓存池的描述,用于管理每个Node的slab页面;
  • struct page:用于描述slab页面,一个slab页面由一个或多个page组成。

他们之间的关系可以用一张图来描述:

流程分析

slab缓存的创建(主要是就是填充kmem_cache结构体成员)。

代码语言:javascript
复制
kmem_cache_create
   create_cache
      kmem_cache_zalloc //分配kmem_cache结构
         kmem_cache_alloc
            slab_alloc
                  slab_alloc_node
      __kmem_cache_create //初始化kmem_cache结构
         kmem_cache_open
      list_add //将创建的kmem_cache添加到全局链表slab_caches中,构成slab缓存池

kmem_cache_create 主要三步:

  1. 分配kmem_cache结构。
  2. 初始化kmem_cache结构。
  3. 将创建的kmem_cache添加到全局链表slab_caches中,构成slab缓存池。

object对象的分配。

代码语言:javascript
复制
kmem_cache_alloc
    slab_alloc
        slab_alloc_node
            if (kmem_cache_cpu里的freelist没有可用的object)
                __slab_alloc
                    ___slab_alloc
                      slub_percpu_partial  //2.接着去 kmem_cache_cpu->partital链表中分配,如果此链表为null
                      new_slab_objects
                          get_partial  //3.接着去 kmem_cache_node->partital链表分配,如果此链表为null
                          new_slab  //4.这就需要重新分配一个slab了。
                              allocate_slab 
            else
                get_freepointer_safe  //1.先从 kmem_cache_cpu->freelist中分配,如果freelist为null

kmem_cache_alloc 主要四步:

  1. 先从 kmem_cache_cpu->freelist中分配,如果freelist为null
  1. 接着去 kmem_cache_cpu->partital链表中分配,如果此链表为null
  1. 接着去 kmem_cache_node->partital链表分配,如果此链表为null
  1. 重新分配一个slab。

kmalloc

虽然刚开始我们通过一个小实验了解了slab的用法,但是实际工作中什么时候会用到呢?其实我们在写驱动的时候经常用,那就是kmalloc。

kmalloc的内存分配就是基于slab分配器,系统在启动的时候会调用create_kmalloc_caches,来创建不同大小的kmem_cache,并将这些kmem_cache存储在kmalloc_caches全局变量中以备后面kmalloc的使用。各个kmem_cache的名字和大小如下所示:

代码语言:javascript
复制
const struct kmalloc_info_struct kmalloc_info[] __initconst = {
 {NULL,                      0},  {"kmalloc-96",             96},
 {"kmalloc-192",           192},  {"kmalloc-8",               8},
 {"kmalloc-16",             16},  {"kmalloc-32",             32},
 {"kmalloc-64",             64},  {"kmalloc-128",           128},
 {"kmalloc-256",           256},  {"kmalloc-512",           512},
 {"kmalloc-1024",         1024},  {"kmalloc-2048",         2048},
 {"kmalloc-4096",         4096},  {"kmalloc-8192",         8192},
 {"kmalloc-16384",       16384},  {"kmalloc-32768",       32768},
 {"kmalloc-65536",       65536},  {"kmalloc-131072",     131072},
 {"kmalloc-262144",     262144},  {"kmalloc-524288",     524288},
 {"kmalloc-1048576",   1048576},  {"kmalloc-2097152",   2097152},
 {"kmalloc-4194304",   4194304},  {"kmalloc-8388608",   8388608},
 {"kmalloc-16777216", 16777216},  {"kmalloc-33554432", 33554432},
 {"kmalloc-67108864", 67108864}
};

我们来看看kmalloc的实现:

代码语言:javascript
复制
static __always_inline void *kmalloc(size_t size, gfp_t flags)
{
 if (__builtin_constant_p(size)) {
  if (size > KMALLOC_MAX_CACHE_SIZE)
   return kmalloc_large(size, flags);
#ifndef CONFIG_SLOB
  if (!(flags & GFP_DMA)) {
   //查找符合满足分配大小的最小kmem_cache
   int index = kmalloc_index(size);

   if (!index)
    return ZERO_SIZE_PTR;

   //将index作为下表从kmalloc_caches数组中找到符合的kmem_cache,并从slab缓存池中分配对象
   return kmem_cache_alloc_trace(kmalloc_caches[index],
     flags, size);
  }
#endif
 }
 return __kmalloc(size, flags);
}
  1. 先是通过kmalloc_index查找符合满足分配大小的最小kmem_cache
代码语言:javascript
复制
static __always_inline int kmalloc_index(size_t size)
{
 if (!size)
  return 0;

 if (size <= KMALLOC_MIN_SIZE)
  return KMALLOC_SHIFT_LOW;

 if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
  return 1;
 if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
  return 2;
 if (size <=          8) return 3;
 if (size <=         16) return 4;
 if (size <=         32) return 5;
 if (size <=         64) return 6;
 if (size <=        128) return 7;
 if (size <=        256) return 8;
 if (size <=        512) return 9;
 if (size <=       1024) return 10;
 if (size <=   2 * 1024) return 11;
 if (size <=   4 * 1024) return 12;
 if (size <=   8 * 1024) return 13;
 if (size <=  16 * 1024) return 14;
 if (size <=  32 * 1024) return 15;
 if (size <=  64 * 1024) return 16;
 if (size <= 128 * 1024) return 17;
 if (size <= 256 * 1024) return 18;
 if (size <= 512 * 1024) return 19;
 if (size <= 1024 * 1024) return 20;
 if (size <=  2 * 1024 * 1024) return 21;
 if (size <=  4 * 1024 * 1024) return 22;
 if (size <=  8 * 1024 * 1024) return 23;
 if (size <=  16 * 1024 * 1024) return 24;
 if (size <=  32 * 1024 * 1024) return 25;
 if (size <=  64 * 1024 * 1024) return 26;
 BUG();

 /* Will never be reached. Needed because the compiler may complain */
 return -1;
}
  1. 然后将index作为下表从kmalloc_caches数组中找到符合的kmem_cache,并从slab缓存池中分配对象。 此时和上面讲的slab缓存池就联系起来了。
代码语言:javascript
复制
static __always_inline void *kmem_cache_alloc_trace(struct kmem_cache *s,
  gfp_t flags, size_t size)
{
 void *ret = kmem_cache_alloc(s, flags);

 kasan_kmalloc(s, ret, size, flags);
 return ret;
}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 人人都是极客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 走进slub
  • 数据结构
  • 流程分析
    • slab缓存的创建(主要是就是填充kmem_cache结构体成员)。
      • object对象的分配。
      • kmalloc
      领券
      问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档