首页 > 系统相关 >内存管理-15-slab、slob和slub分配器-初探

内存管理-15-slab、slob和slub分配器-初探

时间:2024-07-06 10:31:17浏览次数:21  
标签:15 分配器 kmem cache stu 内存 slob slab struct


一、slab简介

1. 简介

首先,“slab”已成为一个通用名称,指的是使用对象缓存的内存分配策略,可实现内核对象的高效分配和释放。它最初由 Sun 工程师 Jeff Bonwick 记录下来,并在 Solaris 2.4 内核中实现。

Linux 目前为其“slab”分配器提供了三种选择:

Slab 是最初的分配器,基于 Bonwick 的开创性论文,自 Linux-2.2 开始可用。它是 Bonwick 提案的忠实实现,并由 Bonwick 的后续论文2 中描述的多处理器更改进行了增强。

Slub 是下一代替代内存分配器,自 Linux-2.6.23 以来一直是内核中的默认分配器。它继续采用基本的“slab”模型,但修复了 Slab 设计中的几个缺陷,特别是在具有大量处理器的系统周围。Slub 比 Slab 更简单。

Slob(简单块列表)是一种针对内存非常少(大约兆字节)的嵌入式系统进行优化后的内存分配器。它对块列表应用非常简单的首次适合算法,与旧的 K&R 式堆分配器类似。Slob 消除了内存分配器中几乎所有的过载,非常适合内存极其受限的系统,但它不提供 Slab/Slub 中描述的任何好处,并且可能会出现病态碎片。

2. 概述

slab机制一般和伙伴系统配合使用,它是对伙伴系统的改进和补充。伙伴系统对物理内存的管理是以page为单位的,粒度比较大。slab是将页拆分成小内存块进行管理。slab首先通过伙伴系统的接口向伙伴系统申请一个或多个物理页,然后将其切割成固定大小的块,缓存起来,当分配此大小的内存块的时候,直接返回缓存的内存块,用户释放时,会释放给slab继续缓存起来。slab中的内存块使用链表链起来。对于常用的结构体,比如 task_struct,可以提前创建slab缓存,使用时直接去拿即可。

有3种类似的分配器,slab、slob、slub

slab: 是内核中最早出现的分配器,随着内核的发展,其实现越来越臃肿,
slob: 面向小型嵌入式系统很少内存的简单管理,比如32MB.
slub: 是一个轻量级的slab,一般使用在嵌入式系统中。

这三种可以通过内核deconfig进行配置,配置不同结构体定义和函数走的分支不同。

CONFIG_SLAB //没使能
CONFIG_SLOB //没使能
CONFIG_SLUB //使能
CONFIG_SLUB_DEBUG //使能
CONFIG_SLUB_CPU_PARTIAL //使能

msm-4.14 和 msm-5.4 都是这样的配置。


二、相关数据结构

kmem_cache
kmem_cache_cpu //每cpu变量
kmem_cache_node

struct kmem_cache_cpu {
    /* 每个内存块内部也占用一部分空间,作为一个指针指向下一个内存块,组成一个单链表,
     * 将所有的空闲内存块链接起来。此变量时刻指向下一个可用的内存块。
     */
    void **freelist;
}

struct kmem_cache_node {
    /* 上面串联的都是大小相同的内存块 */
    struct list_head partial;
}

如下,通过这三个结构体就把slab的框架给搭建起来了。

kmem_cache 每个大小的slab内存块都会使用一个 kem_cache 进行管理。所有的 kmem_cache 使用一个双循环链表链接起来,链表头是全局变量 slab_caches。申请内存时,通过这个全局链表头就可以找到要申请大小对应的 kmem_cache,进而找到其 partial 链表,拿到对应大小的内存块。

kmem_cache_cpu 是个每cpu的变量,每个cpu都缓存一部分slab, 也就是一个page,这样就可以避免链表访问拿锁。当这个page上的slab内存块被使用完了后,重新通过伙伴系统申请一个page来使用。

当申请slab缓存的时候,优先从cpu本地缓存上申请,即先从 kmem_cache_cpu 的 page 内申请,没有的话再看 partial 链表上是否有,若也没有的话才会去全局链表上去申请,若全局链表上也没有,则会向伙伴系统申请物理页再次创建slab缓存


三、相关文件节点

1. /proc/slabinfo

/proc/slabinfo  里面的 <pagesperslab> 表示每个slab占几个物理页。


四、编程接口

/* 创建一个 kmem_cache, 并对每一个内存块调用ctor回调 */
struct kmem_cache *kmem_cache_create(const char *name, unsigned int size, unsigned int align, slab_flags_t flags,
            void (*ctor)(void *)) //slab_common.c
struct kmem_cache *kmem_cache_create_usercopy(const char *name, unsigned int size, unsigned int align,
            slab_flags_t flags, unsigned int useroffset, unsigned int usersize,
            void (*ctor)(void *)) //slab_common.c TODO: 看它两个的区别,视频上说,用户空间经常使用

/* 销毁一个 kmem_cache */
void kmem_cache_destroy(struct kmem_cache *s) //slab_common.c

/* 从 kmem_cache 对应的slab中分配一个object */
void *kmem_cache_alloc(struct kmem_cache *s, gfp_t gfpflags) //slub.c

/* 释放object给原先的slab */
void kmem_cache_free(struct kmem_cache *s, void *x) //slub.c

slab_flags_t 比如 SLAB_CACHE_DMA
ctor: 当创建完kmem_cache后,会回调这个函数,可以在这个函数中进行结构体成员初始化,当一次性创建多个slab块时,此函数会多次针对每个块都进行调用。

若是需要频繁申请释放一个大小的内存块,你也可以使用 kmem_cache_create 创建一个slab.

注意:写上标志的作用。注意这几个机制的文件是如何组织的


五、实验

1. 创建slab缓存

#include <linux/slab.h>

/* 32B */
struct student {
    int id;
    int age;
    float score;
    void (*print_score)(int id);
    void (*print_age)(int id);
};

struct mm_test_2 {
    int unique_id;
    struct kmem_cache *stu_c;
    struct student *stu_p;
};

struct mm_test_2 mt2;

void stu_init(void *p) {
    struct student *stu_p = p;
    stu_p->id = ++mt2.unique_id;
    pr_info("%s called, stu_p->id=%d\n", __func__, stu_p->id); //打印从1--22
}

static void mem_test_2_init(void)
{
    mt2.stu_c = kmem_cache_create("student", sizeof(struct student), 0, SLAB_PANIC, stu_init);
    if (!mt2.stu_c) {
        pr_info("%s NOMEM\n", __func__);
        return;
    }
    mt2.stu_p = kmem_cache_alloc(mt2.stu_c, GFP_KERNEL);
    if (mt2.stu_p) {
        pr_info("sizeof(*mt2.stu_p)=%d, sizeof(struct student)=%d\n", sizeof(*mt2.stu_p), sizeof(struct student)); //32 32
    }
}

static void mem_test_2_exit(void)
{
    kmem_cache_free(mt2.stu_c, mt2.stu_p);
    kmem_cache_destroy(mt2.stu_c);
}

实验结果:

# dmesg -c | grep mm_test
[  221.397263] (3)[6366:sh]mm_test: stu_init called, stu_p->id=1
[  221.397429] (3)[6366:sh]mm_test: stu_init called, stu_p->id=2
...
[  221.401152] (1)[6366:sh]mm_test: stu_init called, stu_p->id=22
[  221.401332] (1)[6366:sh]mm_test: sizeof(*mt2.stu_p)=32, sizeof(struct student)=32

# cat /proc/slabinfo | grep student
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
student                1     22    368   22    2 : tunables    0    0    0 : slabdata      1      1      0

TODO: objsize 为啥差那么多(32/368)?

 

 

参考:
图解slub:http://www.wowotech.net/memory_management/426.html //蜗蜗 TODO
linux内核之slob、slab、slub: https://blog.csdn.net/Rong_Toa/article/details/106440497 //优秀,有英文文档 TODO

 

标签:15,分配器,kmem,cache,stu,内存,slob,slab,struct
From: https://www.cnblogs.com/hellokitty2/p/18286966

相关文章

  • 代码随想录day15 平衡二叉树 | 二叉树的所有路径 | 左叶子之和 | 完全二叉树的节点个
    平衡二叉树平衡二叉树解题思路二叉树节点的深度:指从根节点到该节点的最长简单路径边的条数。二叉树节点的高度:指从该节点到叶子节点的最长简单路径边的条数。这道题由于需要求节点的高度差来进行判断,因此我们需要用后序遍历,先左右,后中间。推荐使用递归把每个节点的高度算出来......
  • Linux remoteproc子系统(基于STM32MP157)概览
    remoteproc(RemoteProcessorFramework)用于管理异构远程处理器设备。这些设备通常在非对称多处理(AsymmetricMultiProcessing,AMP)配置中,可能运行不同的操作系统实例,包括Linux或其他实时操作系统的变体。remoteproc框架允许不同平台或架构控制远程处理器(例如,开启电源、加载固件......
  • c++ primer plus 第15章友,异常和其他:15.1.2 友元成员函数
    #c++primerplus第15章友,异常和其他:15.1.2友元成员函数提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加例如:15.1.2友元成员函数提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录前言15.1.2友元成员函数程序清单15.4tvfm......
  • c++ primer plus 第15章友,异常和其他:15.1.3 其他友元关系
    c++primerplus第15章友,异常和其他:15.1.3其他友元关系提示:这里可以添加系列文章的所有文章的目录,目录需要自己手动添加15.1.3其他友元关系提示:写完文章后,目录可以自动生成,如何生成可参考右边的帮助文档文章目录c++primerplus第15章友,异常和其他:15.1.3其他......
  • 代码随想录算法训练营第十三天|今天量大管饱144、145、94、102、107、199、637、429、
    今天来处理二叉树part1、2、3,顶级享受,一次到位。完全二叉树和满二叉树概念没问题。二叉搜索树,左子树所有结点的值小于它的根结点的值,右子树上所有结点的值大于它的根结点的值平衡二叉搜索树,它是一棵空树或它的左右两个子树的高度差的绝对值不超过1。二叉树的存储方式:链式存储......
  • SP15620 POSTERIN - Postering 题解
    题目传送门前置知识单调栈解法容易有每个建筑物的宽度对答案没有影响,故可以将其宽度均看作\(1\)。在最优策略下,对于每张海报,其高度一定等于所覆盖的楼的最小高度。单调栈维护最小高度,记录额外海报数量(与先前高度相等时可以少用一张海报)。最终,用总张数\(n\)减去额外海报......
  • 【ESP32】打造全网最强esp-idf基础教程——15.WiFi连接STA模式
    WiFi连接STA模式一、ESP32的WiFi功能介绍    前面章节内容,基本上都是描述了ESP32强大的MCU能力,这些MCU能力使得ESP32可以替换许多类型的单片机工作,而自己承担这部分功能;当然ESP32的IOT能力才是它的主业,从硬件配置来看,ESP32支持2.4GHz频段WiFi+BT(LE)4.2,而esp-idf对WiFi......
  • 代码随想录算法训练营第七天| 454. 两数相加Ⅱ、383.赎金信、15.三数之和、18.四数之
    454题拆成两块各自匹配化成两个O(n^2)运算1classSolution{2public:3intfourSumCount(vector<int>&nums1,vector<int>&nums2,vector<int>&nums3,vector<int>&nums4){4//四个数组拆分成两块两块5unordered_ma......
  • 代码随想录算法训练营第九天|151.反转字符串中的单词、55.右旋字符串、28.找出字符串
    151以前写过很呆的写法但能用嘿1classSolution{2public:3stringreverseWords(strings){4//初始化变量5vector<vector<int>>data;//存储单词的起始地址和长度6stringans;//最终结果字符串7intnum=0;......
  • 【适用于各种工业应用】IMLT65R050M2H IMLT65R040M2H IMLT65R015M2H IMLT65R060M2H Co
    摘要CoolSiC™650VG2MOSFET可通过降低能耗来充分利用碳化硅的性能,从而在功率转换过程中实现更高效率。这些CoolSiC650VG2MOSFET适用于各种功率半导体应用,如光伏、能量存储、电动汽车直流充电、电机驱动器和工业电源。配备CoolSiCG2的电动汽车用直流快速充电站与前几代产品......