内存管理机制SLAB

news2025/3/11 10:06:32

1. 为什么需要内存分配管理?为什么需要SLAB?

  • 在学习c语言时,我们常常会使用到malloc()去申请一块内存空间,用于存放我们的数据,这是代码层面的语言

  • 如果我们想要关心malloc这个命令向系统发出后,系统会做什么呢?系统会给这个变量分配一个内存空间。那么系统是如何分配的呢,这就需要了解系统的内存分配管理方法了。

  • 在linux中,最先推出用于分配内存的管理单元和算法是伙伴分配器(buddy allocator),它是以页为单位管理和分配内存,最小分配一页,也就是4KB 大小。而可能内核的需求只是以字节为单位。

    • 假如我们需要动态申请一个内核结构体(占 20 字节),若仍然分配一页内存,这将严重浪费内存。这也将会导致内部碎片问题
  • 这时候就出现了slab分配器,它专门用于分配小内存,分配内存以字节为单位,基于伙伴分配器的大内存进一步细分成小内存分配。

    • 概括来讲:slab 分配器仍然从buddy分配器中申请内存,之后自己对申请来的内存细分管理。从而达到减少内存碎片化的目的。
    • 形象概括:buddy分配器理解成一个仓库,slab分配器理解为一个商店,仓库给商店进行批发的货物,商店从仓库进货以后,再零售给消费者(使用kmalloc的用户)
      在这里插入图片描述
  • SLAB分配器内存管理机制的核心思想:

    • 提供小内存,减少内存碎片
    • 维护常用对象的缓存
    • 提高CPU硬件缓存的利用率
  • 随着时间的推移,SLAB分配器演变成SLUB和SLOB分配器

2. SLAB底层机制

2.1 基本概念

2.1.1 Slab

  • 是SLAB机制中的基本组成单元,它是预先分配的一块连续的内存区域
  • 每个Slab由一个或多个大小相同的对象组成,这些对象属于同一个Cache。
  • Slab可以处于三种状态之一:满(full),部分满(partial)和空(empty)。
  • 系统优先从部分满的Slab中分配对象,以提高内存利用率。

2.1.2 Slab Cache

  • Slab Cache专门用于管理一种特定大小和类型的对象。
  • 每个Slab Cache都由多个Slab组成,它们共同组成了该类型对象的存储池。
  • 通过Slab Cache,系统可以快速地分配和释放对象,避免了每次分配时都进行昂贵的内存搜索和设置操作。
  • 主要负责三个事情:
    • 对象缓存:缓存常用对象,加快分配速度。
    • 内存管理:根据需要增加或释放Slab,优化内存使用。
    • 碎片最小化:通过维护大小相同的对象集合,减少内存碎片。

2.1.3 缓冲色彩(Cache Coloring)

  • 缓存色彩是一种用于优化CPU缓存利用率的技术。通过对内存分配的微小调整,它能减少不同Slab中对象的缓存行冲突。

2.1.4 构造器和析构器

  • 为了进一步提高效率,SLAB机制允许为每种类型的对象定义构造器(Constructor)和析构器(Destructor)。构造器在对象第一次被创建时被调用,用于初始化对象。析构器在对象最终被释放回系统前被调用,用于执行必要的清理工作。通过这种方式,SLAB机制确保了资源的有效利用和稳定的性能表现。

2.2 结构定义

2.2.1 slab分配的内存大小

  • 问题:Linux中采用4KB大小的页框作为标准的内存分配单元,在实际应用中,经常需要分配一组连续的页框,而频繁的申请和释放不同大小的连续页框,必然导致在已分配页框的内存块中分散了许多小块的空闲页框,这样,即使这些页框是空闲的,其他需要分配连续页框的应用也很难得到满足。
  • Linux内核引入了伙伴系统算法来避免这种情况。其把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1、2、4、8、16、32、64、128、256、512和1024个连续页框的页框块。最大可以申请1024个连续页框,也即4MB大小的连续空间。
  • 而slab则基于伙伴系统,进一步将页框划分成各个小的内存块,而他的实现则是通过在kmem_cache_init过程中,通过kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE]来建立caches数组
void __init kmem_cache_init(void){
	kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE] = create_kmalloc_cache(
        kmalloc_info[INDEX_NODE].name[KMALLOC_NORMAL],
        kmalloc_info[INDEX_NODE].size,
        ARCH_KMALLOC_FLAGS, 0,
        kmalloc_info[INDEX_NODE].size);
        ...
        create_kmalloc_caches(ARCH_KMALLOC_FLAGS);
}
  • kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE]中的INDEX_NODE即为kmalloc_index,INDEX_NODE取值范围为0-21,分别存放着不同大小的内存caches
static __always_inline unsigned int __kmalloc_index(size_t size, bool size_is_constant){
    /* 0 = zero alloc */
    if (!size)
        return 0;
        
    if (size <= KMALLOC_MIN_SIZE)
        return KMALLOC_SHIFT_LOW;
 
    /* 1 =  65 .. 96 bytes分配65-96bytes的内存大小的块*/
    if (KMALLOC_MIN_SIZE <= 32 && size > 64 && size <= 96)
        return 1;
        
    /* 2 = 129 .. 192 bytes分配65-96bytes的内存大小的块*/
    if (KMALLOC_MIN_SIZE <= 64 && size > 128 && size <= 192)
        return 2;
        
    /* n = 2^(n-1)+1 .. 2^n 分配2^(n-1)+1 .. 2^n大小内存的块内存*/
    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 (!IS_ENABLED(CONFIG_PROFILE_ALL_BRANCHES) && size_is_constant)
        BUILD_BUG_ON_MSG(1, "unexpected size in kmalloc_index()");
    else
        BUG();
    return -1;
}
  • 通过create_kmalloc_cache创建出对应的内存区域,且它们对应着0,96byte,192byte,8byte,16byte,32byte,64byte . . . 2M的连续内存空间。

2.2.2 slab分配的内存类型

  • 内存类型则由kmalloc_caches[KMALLOC_NORMAL][INDEX_NODE]中的KMALLOC_NORMAL决定,可选诸如KMALLOC_NORMAL、KMALLOC_DMA、KMALLOC_CGROUP、NR_KMALLOC_TYPES、KMALLOC_RECLAIM等,也可以由用户自己定义自己的专用内存类型,诸如kvm_vcpu、dquot、signal_cache等等都是其他模块自行定义的内存类型。
enum kmalloc_cache_type {
    /* 对应着kmalloc的内存 */
    KMALLOC_NORMAL = 0,
#ifndef CONFIG_ZONE_DMA
    KMALLOC_DMA = KMALLOC_NORMAL,
#endif

#ifndef CONFIG_MEMCG_KMEM
    KMALLOC_CGROUP = KMALLOC_NORMAL,
#endif

#ifdef CONFIG_SLUB_TINY
    KMALLOC_RECLAIM = KMALLOC_NORMAL,
#else
    KMALLOC_RECLAIM,
#endif

#ifdef CONFIG_ZONE_DMA
    /* 对应着dma-kmalloc的内存 */
    KMALLOC_DMA,
#endif

#ifdef CONFIG_MEMCG_KMEM
    KMALLOC_CGROUP,
#endif

    NR_KMALLOC_TYPES
};

2.2.3 基本结构

在这里插入图片描述

  • kmem_cache数据结构代表一个slab 缓存
  • kmem_cache_cpu表示了每个 CPU 对象的缓存信息。它用于存储每个 CPU 上的缓存数组(array_cache)以及一些与 CPU 相关的缓存统计信息
  • array_cache用于表示该缓存在各个CPU中的slab对象
  • kmem_cache_node用于管理各个内存节点上slab对象的分配
    在这里插入图片描述
kmem_cache
struct kmem_cache {
    struct array_cache __percpu *cpu_cache;  //表示每个cpu中的slab对象
 
    unsigned int batchcount; 
    //当cpu_cache为空时,从缓存slab中获取的对象数目,它还表示缓存增长时分配的对象数目。
    //初始时为1,后续会调整
    
    unsigned int limit; 
    //cpu_cache中的对象数目上限
    //当slab free达到limit时,需要将array_caches中的部分obj返回到kmem_cache_node的页帧中
    
    unsigned int shared; //表示该缓存是否是共享的
 
    unsigned int size; //表示slab中的每个对象大小
    
    struct reciprocal_value reciprocal_buffer_size; //用于存储一个缓存的倒数大小的数据结构
 
    slab_flags_t flags; //用于存储常量标志的位掩码
    unsigned int num;   //每个slab中的对象数目
 
    unsigned int gfporder; //slab关联页数
 
    gfp_t allocflags;  //强制使用的 GFP 标志,例如 GFP_DMA
 
    size_t colour;   //缓存颜色范围
    unsigned int colour_off;  //颜色偏移量
    
    struct kmem_cache *freelist_cache; // 空闲对象管理
    unsigned int freelist_size; // 空闲对象数量
 
    //构造函数指针
    void (*ctor)(void *obj); //这个在2.6之后已经废弃了
 
    const char *name;      //缓存名称
    struct list_head list; //用于将缓存连接到全局缓存列表的链表节点
    int refcount;          //引用计数
    int object_size;       //对象的大小
    int align;             //对齐方式

#ifdef CONFIG_DEBUG_SLAB
    unsigned long num_active;       //活动对象的数量
    unsigned long num_allocations;  //分配的对象数量
    unsigned long high_mark;        //高水位标记
    unsigned long grown;            //已增长的对象数量
    unsigned long reaped;           //已收割的对象数量
    unsigned long errors;           //错误数量
    unsigned long max_freeable;     //最大可释放的空闲数量
    unsigned long node_allocs;      //节点分配数量
    unsigned long node_frees;       //节点释放数量
    unsigned long node_overflow;    //节点溢出数量
    atomic_t allochit;              //分配命中计数 
    atomic_t allocmiss;             //分配未命中计数
    atomic_t freehit;               //释放命中计数
    atomic_t freemiss;              //释放未命中计数
    
#ifdef CONFIG_DEBUG_SLAB_LEAK
    atomic_t store_user_clean;
#endif

    int obj_offset; //对象偏移量
#endif 
 
#ifdef CONFIG_MEMCG
    struct memcg_cache_params memcg_params; //用于内存控制组的参数
#endif

#ifdef CONFIG_KASAN
    struct kasan_cache kasan_info; //KASan 相关信息
#endif
 
#ifdef CONFIG_SLAB_FREELIST_RANDOM
    unsigned int *random_seq;  //用于slab freelist随机化的随机序列
#endif
 
    unsigned int useroffset;   //用户复制区域的偏移量
    unsigned int usersize;     //用户复制区域的大小
 
    struct kmem_cache_node *node[MAX_NUMNODES];  
    //每个内存节点上的slab对象信息,每个node上包括部分空闲,全满以及全部空闲三个队列
};
keme_cache_cpu
struct kmem_cache_cpu{
	void **freelist;        //指向下一个可用的object
	unsigned long tid;      //全局独一无二的事物ID
	struct page *page;      //slab内存的page指针

#ifdef CONFIG_SLUB_CPU_PARTIAL
	struct page *partial;   //本地slab partial链表,主要是一些部分使用object的slab
#endif
};
array_cache
  • array_cache是一个per_cpu数组,访问不需要加锁,是与cpu cache打交道的直接数据结构,每次获取空闲slab对象时都是通过entry[avail--]去获取,当avail==0时,又从kmem_cache_node中获取batchcount个空闲对象到array_cache中。
struct array_cache {
    unsigned int avail; //保存了当前array中的可用数目
    unsigned int limit; //同上
    unsigned int batchcount; //同上
    unsigned int touched; 
    //缓存收缩时置0,缓存移除对象时置1,使得内核能确认在上一次收缩之后是否被访问过
    void *entry[]; 
    //用于存储缓存的条目的数组,大小在运行时动态确定
};     
kmem_cache_node
  • kmem_cache_node用于管理slab(实际对象存储伙伴页帧),其会管理三个slab列表:
    • 部分空闲partial
    • 全部空闲empty
    • 全部占用full
  • array_cache获取batchcount空闲对象时,先尝试从partial分配,如果不够则再从empty分配剩余对象,如果都不够,则需要grow分配新的slab页帧。
struct kmem_cache_node {
    spinlock_t list_lock;       //自旋锁,用于保护缓存节点的链表操作
    unsigned long nr_partial;   //slab节点中slab的数量
    struct list_head partial;   //slab节点的slab partial链表
};
struct page
  • 用于描述slab页面,一个slab页面由一个或多个page组成,page页帧是物理存储地址

2.3 工作原理

2.3.1 对象分配和释放过程

  • 三个指针:

    • current指针,仅指向一个slab
    • partial指针,指向未满slab链表
    • full指针,指向全满slab链表
      在这里插入图片描述
  • 对象分配:使用current slab,若满,从partial指向的slab中取空闲区域,把current指向的slab移到full

      1. 查找合适的Slab Cache:当系统需要一个特定类型的对象时,首先在对应的Slab Cache中查找。
      1. 选择Slab:在找到的Slab Cache中,系统会寻找状态为部分满(Partial)或空(Empty)的Slab。优先选择部分满的Slab,以提高内存利用率。
      1. 分配对象:从选定的Slab中分配一个空闲对象。如果选择的是空Slab,系统会先初始化该Slab,然后分配对象。
      1. 更新Slab状态:分配对象后,更新Slab的状态。如果所有对象都被分配,Slab状态变为满(Full)。
  • 对象释放:若是full则移动到partial,若partial全空则还给buddy分配器

      1. 确定对象所属的Slab:释放对象时,系统首先确定该对象属于哪个Slab。
      1. 释放对象:将对象标记为未使用,返回到Slab的空闲对象池中。
      1. 更新Slab状态:如果释放对象前Slab是满的,则释放后状态变为部分满(Partial)。如果释放后Slab中所有对象都是空闲的,则状态变为空(Empty)。
      1. Slab的回收:如果一个Slab长时间处于空状态,系统可能会决定回收该Slab,释放内存给操作系统。

2.3.2 缓存色彩和内存对齐

  • 缓存色彩:
    • 缓存色彩是一种用于优化CPU缓存利用率的技术。由于CPU缓存行的存在,不同的内存地址可能会映射到同一个缓存行,这种现象称为缓存行冲突。缓存色彩通过在对象的内存地址上加上小的偏移量,使得连续分配的对象不会映射到相同的缓存行上,从而减少缓存行冲突,提高缓存的使用效率。
  • 内存对齐:
    • 内存对齐是指按照一定的边界来分配内存地址,使得数据的存取更加高效。在处理器架构中,对齐的内存访问通常比非对齐的内存访问速度要快。SLAB内存管理机制通过确保对象在内存中正确对齐,提高了数据访问的速度,减少了内存访问时间。
    • 内存对齐的另一个好处是减少了系统的内存碎片。

3. SLAB、SLUB和SLOB

  • SLAB分配器:适用于内存分配和释放频繁,且需要稳定内存使用的环境。它的设计注重减少内存碎片和提高内存利用率,非常适合长时间运行的服务或系统。
  • SLUB分配器:适用于对性能要求高的场景,特别是在多核处理器上。它的设计简化了内存管理的数据结构,减少了锁的竞争,优化了CPU缓存的使用,提供了高效的内存分配。
  • SLOB分配器:适用于内存资源非常有限的环境,如嵌入式系统或老旧的硬件。它的设计优先考虑内存的紧凑使用,尽可能减少内存的浪费。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1569090.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

HTMLCSSJS

HTML基本结构 <html><head><title>标题</title></head><body>页面内容</body> </html> html是一棵DOM树, html是根标签, head和body是兄弟标签, body包括内容相关, head包含对内容的编写相关, title 与标题有关.类似html这种…

cdp集群扩容节点磁盘挂载失败原因和解决办法

问题说明 之前在对cdp集群扩容节点环境配置时 按照文档挂载磁盘 在最后一步挂载时&#xff0c;发现有块磁盘没挂载上去 可以看到本该挂载到/data5目录的磁盘sdf1被挂到了/data1上面 查看挂载点文件 vim /etc/fstab问题分析&#xff1a; 发现之前在设置挂载点的时候挂载目录…

gpt4.0中文版

我愿把这个网站成为全球最强AI网站&#xff01;弄100多个AI伺候你&#xff1f;&#xff1f; 家人们&#xff0c;你们猜我发现了什么牛逼的AI网站&#xff1f;&#xff1f; 直接上图&#xff1a; 编辑 这个网站&#xff0c;聚合了国内外100多个顶尖的AI&#xff0c;包括了Op…

AI技术在金融领域/银行业的应用和风险

前言 随着科技的不断发展&#xff0c;人工智能&#xff08;AI&#xff09;技术已经在各行各业得到了广泛的应用&#xff0c;其中包括银行业。银行业作为经济的重要组成部分&#xff0c;一直在不断地探索和应用新技术&#xff0c;以提升服务效率、风险管理和客户体验。然而&…

解决nvm切换node版本失败的终极办法,亲测有效!

问题 当我们有多个前端项目&#xff0c;但是使用的node版本不相同&#xff0c;这就需要我们在启动不同项目时切换node版本&#xff0c;很麻烦&#xff0c;这时有一个叫nvm的好东西可以帮我们快速切换node版本。 但是&#xff0c;nvm安装有一个巨大的坑点&#xff1a;nvm use …

二、GitLab相关操作

GitLab相关操作 一、组、用户、项目管理1.创建组2.创建项目3.创建用户并分配组3.1 创建用户3.2 设置密码3.3 给用户分配组 二、拉取/推送代码1.配置ssh(第一次需要)1.1 创建一个空文件夹1.2 配置本地仓账号和邮箱1.3 生成ssh公钥密钥1.4 gitlab配置公钥 2.拉取代码3.推送代码3.…

Linux gcc day3

find命令&#xff08;importance&#xff09;&#xff1a; 语法&#xff1a;find pathname -options find /root -name test.c which命令&#xff1a; which [指令] 只搜索指令&#xff0c;在什么位置下 为什么文件夹带有颜色呢&#xff1f; 科普补充alias命令&#xff1a; ali…

理论实践-CPU性能监控工具-uptime-mpstat-pidstat-vmstat-top-ps-perf

CPU 性能工具。 首先&#xff0c;平均负载的案例。我们先用 uptime&#xff0c; 查看了系统的平均负载&#xff1b;而在平均负载升高后&#xff0c;又用 mpstat 和 pidstat &#xff0c;分别观察了每个 CPU 和每个进程 CPU 的使用情况&#xff0c;进而找出了导致平均负载升高的…

刷题之Leetcode35题(超级详细)

35.搜索插入位置 力扣题目链接(opens new window)https://leetcode.cn/problems/search-insert-position/ 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 你可…

【Java八股学习】Redis持久化 思维导图

说明 文章内容通过学习小林Coding内的优质文章后整理而来&#xff0c;整理成思维导图的方式是为了帮助自己理解、记忆和复习。如若侵权请联系删除&#xff0c;再次对小林Coding内的优质文章表示感谢。参考文章如下&#xff1a; AOF 持久化是怎么实现的&#xff1f;RDB 快照是…

【.NET全栈】ZedGraph图表库的介绍和应用

文章目录 一、ZedGraph介绍ZedGraph的特点ZedGraph的缺点使用注意事项 二、ZedGraph官网三、ZedGraph的应用四、ZedGraph的高端应用五、、总结 一、ZedGraph介绍 ZedGraph 是一个用于绘制图表和图形的开源.NET图表库。它提供了丰富的功能和灵活性&#xff0c;可以用于创建各种…

计算机网络:数据链路层 - 可靠传输协议

计算机网络&#xff1a;数据链路层 - 可靠传输协议 可靠传输概念停止-等待协议 SW回退N帧协议 GBN选择重传协议 SR 可靠传输概念 如下所示&#xff0c;帧在传输过程中受到干扰&#xff0c;产生了误码。接收方的数据链路层&#xff0c;通过真伪中的真检验序列 FCS 字段的值&…

在ArcGIS Pro中优雅的制作荧光图

最近在网上看到了荧光图&#xff0c;觉得挺帅气&#xff0c;去网上查询了怎么制作荧光图&#xff0c;发现大部分都是QGIS的教程&#xff0c;作为ArcGIS的死忠用户&#xff0c;决定在ArcGIS Pro中实现&#xff0c;其实挺简单的。 1、软件&#xff1a;ArcGIS Pro3.0 2、点数据&a…

【RISC-V 指令集】RISC-V 向量V扩展指令集介绍(五)- 向量加载和存储

1. 引言 以下是《riscv-v-spec-1.0.pdf》文档的关键内容&#xff1a; 这是一份关于向量扩展的详细技术文档&#xff0c;内容覆盖了向量指令集的多个关键方面&#xff0c;如向量寄存器状态映射、向量指令格式、向量加载和存储操作、向量内存对齐约束、向量内存一致性模型、向量…

8.list容器的使用

文章目录 list容器1.构造函数代码工程运行结果 2.赋值和交换代码工程运行结果 3.大小操作代码工程运行结果 4.插入和删除代码工程运行结果 5.数据存取工程代码运行结果 6.反转和排序代码工程运行结果 list容器 1.构造函数 /*1.默认构造-无参构造*/ /*2.通过区间的方式进行构造…

如何从 Android 和 iPhone 中的 SIM 卡恢复已删除的联系人 [新]

在手机上&#xff0c;我们经常添加联系人&#xff0c;而很少关心联系人是存储在SIM卡中还是手机中。当我们错误删除SIM卡联系人&#xff0c;或者不当取出插入的SIM卡插入新手机时&#xff0c;那些因业务需要而添加的联系人就会消失。这可能会令人沮丧和困惑。因此&#xff0c;您…

基于springboot实现墙绘产品展示交易平台管理系统项目【项目源码+论文说明】计算机毕业设计

基于springboot实现墙绘产品展示交易平台管理系统演示 摘要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本墙绘产品展示交易平台就是在这样的大环境下诞生&…

macbook向日葵忘记密码

macbook向日葵忘记密码 问题&#xff1a;1、切换到下面的目录&#xff1a;2、找到配置文件3、删除该配置文件4、然后就正常了 问题&#xff1a; 向日葵忘记密码的话&#xff0c;即使是卸载了重装也需要输入密码&#xff0c;界面一直如下&#xff1a; 1、切换到下面的目录&am…

浅谈TCP(2):流量控制与拥塞控制

上文浅谈TCP&#xff08;1&#xff09;&#xff1a;状态机与重传机制介绍了TCP的状态机与重传机制。本文介绍流量控制&#xff08;Flow Control&#xff0c;简称流控&#xff09;与拥塞控制&#xff08;Congestion Control&#xff09;。TCP依此保障网络的QOS&#xff08;Quali…

sparksql执行流程

1. SparkSQL的自动优化 我们前面的文章已经说过spark RDD定义好后&#xff0c;执行经过DAG sechduler划分号内存管道、逻辑任务&#xff0c;然后经由task scheduler来分配到具体worker来管理运行&#xff0c;RDD的运行会完全按照开发者的代码执行 如果开发者水平有限&#xff…