文章目录

  • 1 slab分配器原理
  • 2 slab分配器重要数据结构以及组织关系
    • 2.1 slab cache描述符struct kmem_cache
    • 2.2 slab描述符struct page
  • 3.slab分配器中各个重要结构体间的关系总结

linux中的伙伴系统内存分配器是以页为最小粒度来进行内存分配。在实际的应用中,内核存储的大多数obj(如strcut task_strcuct,struct inode等数据结构)往往只需要几字节到几百字节不等。如果这些内核object的内存申请和释放都通过伙伴系统进行管理,会有以下缺点:

  1. 内存资源浪费,页内部碎片化严重
  2. 内存分配效率低

比如内核用伙伴系统为一个128字节的obj分配内存,内核不得不分配一个4k的page来存储该object,该页剩余的3972字节将会被闲置,内存资源被浪费。而且伙伴系统在释放一个内存块后,会立即将该内存块放在对应的freelist上,且该内存块还有可能与freelist上的相邻内存块合并成高一阶的内存块,若此时内核马上又要使用该内存块,则还需要再次从伙伴系统的freelist上去申请对应内存块,这种操作效率是很低的(未合理使用cpu高速缓存)。

为解决上面小块内存分配时遇到的问题,linux内核提出了slab分配机制,该内存分配器不是按页进行内存分配,而是按字节来分配的。

1 slab分配器原理

slab分配器中用到了对象这个概念,所谓对象就是内核中的数据结构以及对该数据结构进行创建和撤销的操作。它的基本思想是将内核中经常使用的对象放到slab缓存中,并且由系统保持为初始的可利用状态。比如进程描述符,内核中会频繁对此数据进行申请和释放。当一个新进程创建时,内核会直接从slab分配器的slab缓存中获取一个已经初始化了的对象;当进程结束时,该结构所占的页框并不被释放,而是重新返回slab分配器中。如果没有基于对象的slab分配器,内核将花费更多的时间去分配、初始化以及释放一个对象。

slab分配器把对象分组放进slab缓存。每个slab缓存都是同种类型对象的一种“储备”。一个slab cache管理一组大小固定的内存块(也称为对象实体),每个内存块都可用作一种数据结构。slab cache中的内存块来自一到多个slab。一个slab来自物理内存管理器的一到多个物理页,该slab被分成一组固定大小的块,被称为slab对象(object),一个slab属于一个slab cache,其中的对象就是该slab cache所管理的固定大小的内存块。所以一个slab cache可以有一到多个slab。

在基于slab的内核内存管理器中,基本的概念是保存管理型数据的缓存(即slab cache)和保存被管理对象的各个slab。每个缓存都负责一种对象类型,比如kmalloc-128缓存负责管理65-128字节内存空间的的分配。系统中的所有缓存类型都保存在一个链表slab_caches中。下图给出了slab分配器的各个组成部分间的相互关系:

slab分配器有以下三个基本目标:

  1. 减少伙伴算法在分配小块连续内存时所产生的内部碎片;
  2. 将频繁使用的对象缓存起来,减少分配、初始化和释放对象的时间开销。
  3. 通过着色技术调整对象以更好的使用硬件高速缓存;

2 slab分配器重要数据结构以及组织关系

2.1 slab cache描述符struct kmem_cache

在slab分配器中slab cache的描述符为struct kmem_cache,它是该机制的核心数据结构。

在slab分配器中,最顶层结构为slab cache,其描述符为struct kmem_cache.linux中每个slab obj对应的slab cache都有自己的名字(如:ext4_inode_cache,kmalloc-8和kmalloc-16等).

struct kmem_cache用于描述一种slab cache,并管理着一个或多个该slab中的的所有obj。linux中所有的kmem_cache会保持在一个以slab_caches作为头节点的链表中。kmem_cache代码定义如下所示。

// mm_slab_def.h
/** Definitions unique to the original Linux SLAB allocator.*/struct kmem_cache {//本地高速缓存,每CPU结构,对象释放时,优先放入这个本地CPU高速缓存中struct array_cache __percpu *cpu_cache;
/* 1) Cache tunables. Protected by slab_mutex *//*batchcount指定了在per-CPU变量中entry列表为空的情况下,从缓存的slab中获取对象的数目。它还表示在*缓存增长时,若entry数组中obj数量超过limit限制后,需要从entry数组中迁移走的obj数量(到本地节点cpu共享空闲链*表中)。*/unsigned int batchcount;//本地高速缓存中entry数组中空闲obj的最大数目unsigned int limit;//CPU共享高速缓存标志,实际地址保存在kmem_cache_node结构中unsigned int shared;//对象长度 + 填充字节unsigned int size;struct reciprocal_value reciprocal_buffer_size;
/* 2) touched by every alloc & free from the backend *///属性的标识,如果SLAB管理结构放在外部,则CFLAGS_OFF_SLAB置1unsigned int flags;       /* constant flags *///每个slab中obj数量unsigned int num;     /* # of objs per slab *//* 3) cache_grow/shrink *//* order of pgs per slab (2^n) *///每个slab页块的阶(一个slab由2^gfporder个页构成)unsigned int gfporder;/* force GFP flags, e.g. GFP_DMA *///从伙伴系统分配页,补足slab时,页分配的gfp码gfp_t allocflags;//一个slab中有多少不同的cache linesize_t colour;          /* cache colouring range *///一个cache colour的长度,和一个cache line的大小相同unsigned int colour_off;    /* colour offset *//**空闲对象链表放在slab外部时使用,管理用于slab对象管理结构中freelist成员的缓存,也就是又一个新缓存*/struct kmem_cache *freelist_cache;//空闲对象链表的大小unsigned int freelist_size;/* constructor func */// 创建高速缓存时的构造函数指针,一半为nullvoid (*ctor)(void *obj);/* 4) cache creation/removal *///slab缓存名字const char *name;//slab缓存描述符双向链表指针struct list_head list;int refcount;//slab中每个obj的大小int object_size;//obj对齐字节int align;
...
...
...//slab节点链表组,对于NUMA系统中每个节点都会有一个struct kmem_cache_node数据结构struct kmem_cache_node *node[MAX_NUMNODES];
};

通过struct kmem_cache的代码实现可以看出,slab cache中所有slab的大小一致,由一个或多个连续页组成(通常为一个page,伙伴系统提供),每个slab中的obj大小和数量也是相同的。

slab cache描述符struct kmem_cache中除了相关的管理数据外,有两个很重要的成员:

  1. struct array_cache __percpu *cpu_cache:

    1. cpu_cache是一个Per-CPU数据结构,每个CPU独享(相当于函数和它局部变量的关系),用来表示本地CPU的slab cache对象缓冲池(注意是slab cache obj缓冲池不是slab cache slab缓冲池)。
    2. CPU都有自己的硬件高速缓存,当前CPU上释放对象时,这个对象很可能还在CPU的硬件高速缓存中,这时使用这个对象的代价是非常小的,不需要重新装载到硬件高速缓存中,离CPU又最近。同时还可以减少锁的竞争,尤其是在多个CPU同时申请同样size或者同个缓存对象时,无需加锁即可操作。
    3. array_cache中的entry空数组,就是用于保存本地cpu刚释放的obj,所以该数组初始化时为空,只有本地cpu释放slab cache的obj后才会将此obj装入到entry数组。array_cache的entry成员数组中保存的obj数量是由成员limit控制的,超过该限制后会将entry数组中的batchcount个obj迁移到对应节点cpu共享的空闲对象链表中。
    4. entry数组的访问机制是LIFO(last in fist out),此种设计非常巧妙,能保证本地cpu最近释放该slab cache的obj立马被下一个slab内存申请者获取到(有很大概率此obj仍然在本地cpu的硬件高速缓存中).
    // mm_slab_def.h
    /** struct array_cache** Purpose:* - LIFO ordering, to hand out cache-warm objects from _alloc* - reduce the number of linked list operations* - reduce spinlock operations** The limit is stored in the per-cpu structure to reduce the data cache* footprint.**/
    struct array_cache {//当前CPU上该slab可用对象数量unsigned int avail;//最大对象数目,和kmem_cache中一样unsigned int limit;/*同kmem_cache,batchcount指定了在per-CPU列表为空的情况下,从缓存的slab中获取对象的数目。它还表示在*缓存增长时,若entry数组中obj数量超过limit限制后,需要迁移entry中obj的数量(到本地节点cpu共享空闲链*表中)。*/unsigned int batchcount;/*在从缓存移除一个对象时,将touched设置为1,而缓存收缩时, 则将touched设置为0。这使得内核能够确认在缓* 存上一次收缩之后是否被访问过,也是缓存重要性的一个标志。*/unsigned int touched;/*最后一个成员entry是一个伪数组, 其中并没有数组项, 只是为了便于访问内存中array_cache实例之后缓存中的*各个对象而已*/void *entry[]; /** Must have this definition in here for the proper* alignment of array_cache. Also simplifies accessing* the entries.*/
    };
    
  2. struct kmem_cache_node *node[MAX_NUMNODES]:

    1. slab缓存会为不同的节点维护一个自己的slab链表,用来缓存和管理自己节点的slab obj,这通过kmem_cache中node数组成员来实现,node数组中的每个数组项都为其对应的节点分配了一个struct kmem_cache_node结构来。

    2. struct kmem_cache_node结构定义的变量是一个每node变量。相比于struct array_cache定义的每cpu变量,kmem_cache_node管理的内存区域粒度更大,因为kmem_cache_node维护的对象是slab,而array_cache维护的对象是slab中的obj(一个kmem_cache可能包含一个或多个slab,而一个slab中可能包含一个或多个slab obj).

    3. 通过下面struct kmem_cache_node结构的代码实现我们来分析该结构体如何实现对本地节点指定slab cache中所有的slab进行管理的。

      //mm/slab.h
      struct kmem_cache_node {//该链表中存储的所有slab中只有部分obj是空闲的struct list_head slabs_partial;  /* partial list first, better asm code *///该链表中存储的所有slab中不存在空闲的objstruct list_head slabs_full;//该链表中存储的所有slab中每个obj都是空闲的struct list_head slabs_free;//该节点中此kmem_cache的slab总数unsigned long num_slabs;//该节点中此kmem_cache空闲obj总数unsigned long free_objects;//该节点中此kmem_cache中空闲obj数量的上限,多了就会回收到伙伴系统的空闲链表中unsigned int free_limit;unsigned int colour_next;   /* Per-node cache coloring *///该节点上所有cpu共享的本地高速缓存struct array_cache *shared;    /* shared per node *///其他节点上所有cpu共享的本地高速缓存struct alien_cache **alien;   /* on other nodes */unsigned long next_reap;    /* updated without locking */int free_touched;      /* updated without locking */
      };
      

      由上代码可以看出,struct kmem_cache_node对于本节点中slab的管理主要分了3个链表:slabs_partial(部分空闲slab链表),非空闲slab链表(slabs_full)和全空闲slab链表(slabs_free).struct kmem_cache_node还会将本地节点中需要节点共享的slab obj缓存在它的shared成员中。若本地节点向访问其他节点贡献的slab obj,可以利用struct kmem_cache_node中的alien成员去获取。

2.2 slab描述符struct page

为了提高linux内存利用率,slab描述符结合在页描述符struct page中,当页描述符成员flage标志设置了PG_SLAB后,struct page中的部分成员可以用来描述slab对象.如下代码所示(由于struct page成员很多,下面只将描述slab对象有关的成员列出):

//为了节省内存,struct page中许多结构体会通过union联合体进行封装,此处做了省略
struct page {/* First double word block *///当页描述符成员flage标志设置了PG_SLAB后,struct page中对应的slab成员才有效unsigned long flags;      /* Atomic flags, some possibly updated asynchronously */void *s_mem;            /* slab first object *///freelist实际上是一个数组,数组中存放slab中未使用的obj的index值void *freelist;        /* sl[aou]b first free object *//* page_deferred_list().prev    -- second tail page *///当前slab已经使用的obj数量unsigned int active;        /* SLAB *//* 通过该成员将链到slab cache对应节点kmem_cache_node所管理的3个链表上*/struct list_head lru;  /* Pageout list, eg. active_list* protected by zone_lru_lock !* Can be used as a generic list* by the page owner.*///指向slab的slab cache描述符kmem_cachestruct kmem_cache *slab_cache;   /* SL[AU]B: Pointer to slab *///slab首地址,待验证void *virtual;}

由上面代码可以看出在slab描述符中最重要两个数据结构是s_mem和freelist这两个成员。

  1. s_mem用于指向指定slab区域中第一个obj(slab由一个或多个连续的页构成,由伙伴系统分配的内存块,固一个slab包含页的个数为2^gfp_order)
  2. freelist指向的是slab空闲obj的索引数组,该数组要和slab描述符中的active成员搭配使用。freelist指向的区域是一个连续地址的数组,它保存的地方有两种情况:一种情况是放在slab自身所在的内存区域,另外一种情况是保存在slab所在内存区域的外部(若保存在slab外部,则保存在slab对应slab cache的kmem_cache结构体中freelist_cache成员指针指向的链表).

下面介绍slab的空闲索引数组保存在slab自身内部的情况slab描述符是如何管理slab和其内部obj,如下图所示:

通过示意图来描述slab如何根据freelist数组和active成员来分配和释放其内部的空闲obj的(假设该slab中有6个obj)。

在slab机制中slab管理的内存区域的连续页块数,和slab cache结构描述符struct kmem_cache中的gfporder有关。该gfp是在slab cache初始化时通过每个slab中obj的数量和大小,着色区大小,freelist大小,slab对象描述符大小等一系列数据计算而来。需要注意的是这些对象的大小并不是创建的时候对象本身的大小,系统为了优化内存访问还会做一些字节对齐的填充,或者是在对象交界处填充一些magicnum进行防越界处理(如red_zone).

3.slab分配器中各个重要结构体间的关系总结

通过上图我们对slab分配器中各个重要结构体间的关系做一个总结:

  1. slab机制中每个slab cache对应一个struct kmem_cache结构体,linux内核中会分配一个静态全局的struct kmem_cache变量slab_caches,该指向一个双向链表,内核中所有的slab cache的结构描述符都会挂到该链表中(通过list成员变量接入slab_caches链表)。我们可以在shell中断通过/proc/slabinfo查看内核中所有slab cache的相关数据信息。

    / # cat /proc/slabinfo
    slabinfo - version: 2.1
    # name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> >
    zswap_entry            0      0    360   22    2 : tunables    0    0    0 : slabdata      0      0      0
    sw_flow_stats          0      0    448   18    2 : tunables    0    0    0 : slabdata      0      0      0..................dma-kmalloc-64         0      0    368   22    2 : tunables    0    0    0 : slabdata      0      0      0
    dma-kmalloc-32         0      0    336   24    2 : tunables    0    0    0 : slabdata      0      0      0
    dma-kmalloc-16         0      0    320   25    2 : tunables    0    0    0 : slabdata      0      0      0
    dma-kmalloc-8          0      0    312   26    2 : tunables    0    0    0 : slabdata      0      0      0
    dma-kmalloc-192        0      0    496   16    2 : tunables    0    0    0 : slabdata      0      0      0
    dma-kmalloc-96         0      0    400   20    2 : tunables    0    0    0 : slabdata      0      0      0
    kmem_cache_node      213    216    448   18    2 : tunables    0    0    0 : slabdata     12     12      0
    kmem_cache           216    224    576   28    4 : tunables    0    0    0 : slabdata      8      8      0
  2. 每个slab cache的描述符(struct kmem_cache)中会包含一个Per-CPU结构struct array_cache,该结构体用来作为对应slab cache对象的本地CPU高速缓存,缓存的对象是slab obj.

  3. 每个slab cache的描述符中还包含了一个struct kmem_cache_node数组成员node:

    1. node数组长度为系统内存节点个数,也就是每个slab cache会给os的每个内存节点分配一个kmem_cache_node结构体(node[nodeid]),kmem_cache_node结构体用来缓存和管理对应内存节点中的所有slab。
    2. 每个kmem_cache_node结构中包含3个链表slabs_partial、slabs_full和slabs_free,每个链表存储的都是对应内存节点中的slab描述符。其中slabs_partial链表中存储的slab既包含空闲的 slab obj,也包含已经被使用的slab obj。slabs_full链表中的slab只包含处于使用状态的slab obj。而slab_free链表中的slab则只包含处于空闲状态的slab
    3. 每个kmem_cache_node结构中还存在一个其他node共享的stuct array_cache成员shared,shared成员中的entry数组存储的是本内存节点中部分的slab obj,这些slab obj能被系统中的其他内存节点共享(本质是其他内存节点对应的cpu也能访问这些slab obj)
  4. slab的描述符共用了页的结构描述符struct page,此时struct page中的flage标志成员置位了PG_SLAB位。

    1. 每个slab cache包含了一个或多个slab,同一个slab cache中的slab指向的内存区域大小一样(2^gfporder),且slab内部包含的slab obj数量也一样。

    2. slab描述符(struct page)中的s_mem成员指向了slab中第一个obj(存储obj的首地址),而freelist成员指向了slab的空闲obj索引数组,它结合描述符中的active成员就能获取第一个空闲obj在s_mem指向内存区域的索引。

    3. slab着色区的作用是为了错开不同的slab,让CPU更有效的缓存slab。

      (1).通过slab着色(slab coloring), slab分配器能够均匀地在缓存中分布对象, 以实现均匀的缓存利用
      (2).经常使用的内核对象保存在CPU高速缓存中,这是我们想要的效果。从`slab`分配器的角度进行衡量, 伙伴系统的高速缓存和TLB占用较大,这是一个负面效应,因为这会导致不重要的数据驻留在`CPU`高速缓存中, 而重要的数据则被置换到内存, 显然应该防止这种情况出现.
      (3).着色这个术语是隐喻性的. 它与颜色无关, 只是表示slab中的对象需要移动的特定偏移量, 以便使对象放置到不同的缓存行.
      

[内核内存] slab分配器1---slab机制详解相关推荐

  1. python内存的回收机制_python的内存管理和垃圾回收机制详解

    简单来说python的内存管理机制有三种 1)引用计数 2)垃圾回收 3)内存池 接下来我们来详细讲解这三种管理机制 1,引用计数: 引用计数是一种非常高效的内存管理手段,当一个pyhton对象被引用 ...

  2. linux内核rcu,linux内核rcu机制详解

    RCU(Read-Copy Update)是数据同步的一种方式,在当前的Linux内核中发挥着重要的作用.RCU主要针对的数据对象是链表,目的是提高遍历读取数据的效率,为了达到目的使用RCU机制读取数 ...

  3. JAVA之JVM垃圾回收(GC)机制详解

    一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因为我们在不断的分配内存空间而不进行回收.除非内存无限大,我们可以任性的分配而不回收,但是事实并非如此.所以,垃圾回收是必须的. 二. ...

  4. Java垃圾回收(GC)机制详解

    Java垃圾回收(GC)机制详解 转自:https://www.cnblogs.com/xiaoxi/p/6486852.html 一.为什么需要垃圾回收 如果不进行垃圾回收,内存迟早都会被消耗空,因 ...

  5. Linux Jump Label/static-key机制详解

    Linux Jump Label/static-key机制详解 RToax 2021年3 关于Linux Jump Label(x86)已经进行过概述,下面就static-key进行详述. 内核中有很 ...

  6. CAS机制详解以及ABA问题的危害和解决方案

    CAS机制详解以及ABA问题的危害和解决方案 首先,先讲一下 CAS机制到底怎么运作.让我们一探究竟. 1.什么是CAS机制 CAS是英文单词Compare And Swap的缩写,翻译过来就是比较并 ...

  7. 存储之Block-MultiQueue机制详解(二)

    1.首先看一下blk-mq的处理流程图(不同内核版本之间会有一些差异,但整体结构基本一样) 从整个流程图可以看到,主要是分为三个部分:(1)初始化硬件设备的相关参数(2)初始化请求队列request_ ...

  8. Broker存储机制详解

    文章目录 Broker详解 Broker概述 Broker启动和停止流程 Broker存储机制 为什么写文件这么快 Broker存储消息流程详解 (1)Broker接收客户端发送消息的请求并做预处理 ...

  9. Binder机制详解(一)

    系列目录 Binder机制详解(二) Binder机制详解(三) 文章目录 前言 一.爱情例子 1.普通Linux间进程通信方式 2.接着讲爱情的例子(Binder的实现机制) 3.Binder少拷贝 ...

最新文章

  1. Linux下新建java程序,编译,运行(以Ubuntu为例)
  2. STM32 电机教程 1 - 用ST Motor Profiler 测量无刷电机参数
  3. gitlab 修改git clone地址为指定域名
  4. Spring框架学习笔记(2)——IOCDI
  5. Jmeter-JDBC Request
  6. android.intent.action.view 融云,Android 融云SDK集成单聊
  7. Java从FTP网盘读取文件
  8. bcc校验位怎么算的_数据BCC校验码计算工具
  9. java学习之自定义异常
  10. linux下测试磁盘的读写IO速度(IO物理测速)
  11. 立林门禁读卡器接线图_立林楼宇对讲维修方法—常见故障的解决方法
  12. 用for循环打印出大写字母的ASCII码对照表
  13. ansible模块--setup模块
  14. 腾讯的职级系统——看清自己的职场宿命
  15. 手机话费充值和手机流量充值 API
  16. 计算机分析学生表字段,巧用Excel数据透视表统计分析学生成绩
  17. KNN算法说明以及sklearn 中 neighbors.KNeighborsClassifier参数说明
  18. 如何让女朋友说Yes I Do,数据分析助力客户管理秘籍
  19. HDOJ 1847Good Luck in CET-4 Everybody!(巴士博弈)
  20. android+广播接收者category,广播接收者

热门文章

  1. win7网络中能看到计算机但无法连接,为什么win7电脑的网络连接正常,但是却无法打开网页?...
  2. 线性时不变系统(LTI)简介
  3. OpenMP相关文章收藏学习
  4. Apollo 应用与源码分析:Monitor监控-软件监控-定位、camera、功能安全、数据记录监控
  5. nb757l400安装说明书_NB NB757-L400(32-60英寸)电视挂架 电视架 电视机挂架 电视支架 旋转伸缩 乐视海信海尔TCL康佳三星夏普LG...
  6. 深入jQuery Mobile
  7. Jenkins 流水线 获取git 分支列表_CI/CD 工具选型:Jenkins 还是 GitLab CI/CD?
  8. Spring boot + RabbitMQ延迟队列实战
  9. 中云融信 SNS携手云计算将解决数据之殇
  10. 用keil刚打开项目后,跳出的与pdsc文件相关的错误解决方法