分析linux内核中的slub内存管理算法

分析linux内核中的slub内存管理算法

1. 分析的linux内核源码版本为4.18.0

2. 与slub相关的内核配置项为CONFIG_SLUB

3. 一切都从一个结构体数组kmalloc_caches开始,它的原型如下:

struct kmem_cache *kmalloc_caches[KMALLOC_SHIFT_HIGH + 1] __ro_after_init;

  3.1 这个数组定义在mm/slab_common.c中

  3.2 KMALLOC_SHIFT_HIGH是如何定义的呢? 

#define KMALLOC_SHIFT_HIGH  (PAGE_SHIFT + 1)
#define PAGE_SHIFT  12 (各个架构下的定义都有些差异,如果是arm64,那么是通过CONFIG_ARM64_PAGE_SHIFT来指定的,这个配置项在arch/arm64/Kconfig文件中定义,默认为12,也就是默认页面大小为4KiB,笔者以arm64为例)

    那么KMALLOC_SHIFT_HIGH=PAGE_SHIFT + 1 = 12 + 1 = 13,KMALLOC_SHIFT_HIGH+1=13+ 1= 14说明kmalloc_caches数组中有14个元素,每个元素是kmem_cache这个结构体

  3.3 分析一下sturct kmem_cache这个结构体

     

  /*
   * Slab cache management.
   */
  struct kmem_cache {
      struct kmem_cache_cpu __percpu *cpu_slab; 
      /* Used for retriving partial slabs etc */
      slab_flags_t flags;
      unsigned long min_partial;
      unsigned int size;  /* The size of an object including meta data */
      unsigned int object_size;/* The size of an object without meta data */
      unsigned int offset;    /* Free pointer offset. */
  #ifdef CONFIG_SLUB_CPU_PARTIAL
      /* Number of per cpu partial objects to keep around */
      unsigned int cpu_partial;
  #endif
      struct kmem_cache_order_objects oo;

      /* Allocation and freeing of slabs */
      struct kmem_cache_order_objects max;
      struct kmem_cache_order_objects min;
      gfp_t allocflags;   /* gfp flags to use on each alloc */
      int refcount;       /* Refcount for slab cache destroy */
      void (*ctor)(void *);
      unsigned int inuse;     /* Offset to metadata */
      unsigned int align;     /* Alignment */
      unsigned int red_left_pad;  /* Left redzone padding size */
      const char *name;   /* Name (only for display!) */
      struct list_head list;  /* List of slab caches */
  #ifdef CONFIG_SYSFS
      struct kobject kobj;    /* For sysfs */
      struct work_struct kobj_remove_work;
  #endif
  #ifdef CONFIG_MEMCG
      struct memcg_cache_params memcg_params;
      /* for propagation, maximum size of a stored attr */
      unsigned int max_attr_size;
  #ifdef CONFIG_SYSFS
      struct kset *memcg_kset;
  #endif
  #endif

 
 #ifdef CONFIG_SLAB_FREELIST_HARDENED
      unsigned long random;
  #endif

  #ifdef CONFIG_NUMA
      /*
       * Defragmentation by allocating from a remote node.
       */
      unsigned int remote_node_defrag_ratio;
  #endif

  #ifdef CONFIG_SLAB_FREELIST_RANDOM
      unsigned int *random_seq;
  #endif

  #ifdef CONFIG_KASAN
      struct kasan_cache kasan_info;
  #endif

      unsigned int useroffset;    /* Usercopy region offset */
      unsigned int usersize;      /* Usercopy region size */

      struct kmem_cache_node *node[MAX_NUMNODES];
  };

  3.4 struct kmem_cache中有哪些域是需要关注到的呢?

    3.4.1 node

      struct kmem_cache_node *node[MAX_NUMNODES]; 

      这里面MAX_NUMNODES定义如下:

    

      #define MAX_NUMNODES    (1 << NODES_SHIFT)

      那么NODES_SHIFT又是如何定义的呢?

    

      #ifdef CONFIG_NODES_SHIFT
      #define NODES_SHIFT     CONFIG_NODES_SHIFT
      #else
      #define NODES_SHIFT     0
      #endif

      如果定义了CONFIG_NODES_SHIFT,那么NODES_SHIFT就等于CONFIG_NODES_SHIFT的值;

      如果未定义CONFIG_NODES_SHIFT,那么NODES_SHIFT就等于0;

      假设未定义CONFIG_NODES_SHIFT,那么MAX_NUMNODES就等于1,也就是只有一个kmem_cache_node节点.

    3.4.2 cpu_slab

      

      struct kmem_cache_cpu __percpu *cpu_slab;

      表示每个cpu都具有一个这个的结构来描述当前slab的情况

      

      __percpu是什么?

      

      # define __percpu   __attribute__((noderef, address_space(3)))

      __percpu表示一种特性,是用来修饰变量的.noderef指定这个变量必须是有效的,address_space(3)则指定变量所在的地址空间为3,也就是cpu空间.作用就是保证每个cpu都有这个变量的副本

    3.4.3 size

      

      unsigned int size;  /* The size of an object including meta data */

      表示包含元数据的一个object的大小

    3.4.4 object_size

      

      unsigned int object_size;/* The size of an object without meta data */

      表示不包含元数据的一个object的大小

    3.4.5 offset

      
      unsigned int offset;    /* Free pointer offset. */

      表示空闲指针的偏移量

  3.5 __ro_after_init是什么东西?      

    #define __ro_after_init __attribute__((__section__(".data..ro_after_init")))

    这是一个宏,定义在include/linux/cache.h中,被用来标记初始化之后只读的内容

    这里面涉及到一个段.data..ro_after_init,可以在include/asm-generic/vmlinux.lds.h中找到   

1            #ifndef RO_AFTER_INIT_DATA
2     #define RO_AFTER_INIT_DATA  
3     __start_ro_after_init = .;  
4     *(.data..ro_after_init) 
5     __end_ro_after_init = .;
6     #endif        
View Code

4.  如何填充kmalloc_caches数组的呢?

  start_kernel()-> (init/main.c)

      mm_init()->  (init/main.c)

        kmem_cache_init()->  (mm/slub.c)

          create_kmalloc_caches()-> (mm/slab_common.c)

            new_kamalloc_cache()-> (mm/slab_common.c)

              create_kmalloc_cache()-> (mm/slab_common.c)

  从源码中可以得知kmalloc_caches数组由create_kmalloc_cache()填充每一个数组中的元素

5. slub中支持的object的大小范围是多少?

  每个kmalloc_caches中的元素会使用结构体kmem_cache中的域size和objsize来指定slab中每个object的大小,object的大小从全局常量结构体数组kmalloc_info中获取

  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}
  };
View Code

  从数组中的最后一个元素可以获知支持的最大object的大小为2^26=64MiB,但是从以下代码分析:

  void __init create_kmalloc_caches(slab_flags_t flags)
  {
      int i;

      for (i = KMALLOC_SHIFT_LOW; i <= KMALLOC_SHIFT_HIGH; i++) {
          if (!kmalloc_caches[i])
              new_kmalloc_cache(i, flags);

  可得:

    kmalloc_caches数组中仅下标为KMALLOC_SHIFT_LOW到KMALLOC_SHIFT_HIGH的元素才被初始化,

    也就是从3->13支持的最小object大小为2^3=8字节,最大object大小为2^13=8KiB,说明kmalloc_caches数组的前三个元素并没有被初始化,仅初始化数组的后面11个元素.

    从以上分析可得slub支持的最大object的大小为页面大小的两倍(PAGE_SIZE*2),页面大小为4KiB,那么最大object的大小为4KiB * 2 = 8KiB