分析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

                  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM