slab 内存池的设计与实现

news2024/9/22 15:31:41

目录

从一个简单的内存页开始聊 slab

 slab 的总体架构设计

slab 的组织架构

​编辑

​编辑 

 参考文献


伙伴系统内存分配原理的相关内容来看,伙伴系统管理物理内存的最小单位是物理内存页 page。也就是说,当我们向伙伴系统申请内存时,至少要申请一个物理内存页。

内核实际运行过程中来看,无论是从内核态还是从用户态的角度来说,对于内存的需求量往往是以字节为单位,通常是几十字节到几百字节不等,远远小于一个页面的大小。如果我们仅仅为了这几十字节的内存需求,而专门为其分配一整个内存页面,这无疑是对宝贵内存资源的一种巨大浪费。

于是在内核中,这种专门针对小内存的分配需求就应运而生了,而本文的主题—— slab 内存池就是专门应对小内存频繁的分配和释放的场景的。

slab 首先会向伙伴系统一次性申请一个或者多个物理内存页面,正是这些物理内存页组成了 slab 内存池。

这种小内存在内核中的使用场景非常之多,比如,内核中那些经常使用,需要频繁申请释放的一些核心数据结构对象:task_struct 对象,mm_struct 对象,struct page 对象,struct file 对象,socket 对象等。

事实上,凡是需要被内核频繁使用的内核对象都需要被 slab 对象池所管理。

从一个简单的内存页开始聊 slab

slab 对象池在内存管理系统中的架构层次是基于伙伴系统之上构建的,slab 对象池会一次性向伙伴系统申请一个或者多个完整的物理内存页,在这些完整的内存页内在逐步划分出一小块一小块的内存块出来,而这些小内存块的尺寸就是 slab 对象池所管理的内核核心对象占用的内存大小。

因为对象在 slab 中没有被分配出去使用的时候,其实对象所占的内存中存放什么,用户根本不会关心的。既然这样,内核干脆就把指向下一个空闲对象的 freepointer 指针直接存放在对象所占内存(object size)中,这样避免了为 freepointer 指针单独再分配内存空间。巧妙的利用了对象所在的内存空间(object size)。

内核为了应对内存读写越界的场景,于是在对象内存的周围插入了一段不可访问的内存区域,这些内存区域用特定的字节 0xbb 填充,当进程访问的到内存是 0xbb 时,表示已经越界访问了。这段内存区域在 slab 中的术语为 red zone,大家可以理解为红色警戒区域。

 当 slab 刚刚从伙伴系统中申请出来,并初始化划分物理内存页中的对象内存空间时,内核会将对象的 object size 内存区域用特殊字节 0x6b 填充,并用 0xa5 填充对象 object size 内存区域的最后一个字节表示填充完毕。或者当对象被释放回 slab 对象池中的时候,也会用这些字节填充对象的内存区域。

这种通过在对象内存区域填充特定字节表示对象的特殊状态的行为,在 slab 中有一个专门的术语叫做 SLAB_POISON (SLAB 中毒)。POISON 这个术语起的真的是只可意会不可言传,其实就是表示 slab 对象的一种状态。

是否毒化 slab 对象是可以设置的,当 slab 对象被 POISON 之后,那么会有一个问题,就是我们前边介绍的存放在对象内存区域 object size 里的 freepointer 就被会特殊字节 0x6b 覆盖掉。这种情况下,内核就只能为 freepointer 在额外分配一个 word size 大小的内存空间了。

 slab 对象的内存布局信息除了以上内容之外,有时候我们还需要去跟踪一下对象的分配和释放相关信息,而这些信息也需要在 slab 对象中存储,内核中使用一个 struct track 结构体来存储跟踪信息。

这样一来,slab 对象的内存区域中就需要在开辟出两个 sizeof(struct track) 大小的区域出来,用来分别存储 slab 对象的分配和释放信息。

 

 slab 的总体架构设计

slab cache 在内核中的数据结构为 struct kmem_cache,以上介绍的这些 slab 的基本信息以及 slab 的管理结构全部定义在该结构体中:


/*
 * Slab cache management.
 */
struct kmem_cache {
    // slab cache 的管理标志位,用于设置 slab 的一些特性
    // 比如:slab 中的对象按照什么方式对齐,对象是否需要 POISON  毒化,是否插入 red zone 在对象内存周围,是否追踪对象的分配和释放信息 等等
    slab_flags_t flags;
    // slab 对象在内存中的真实占用,包括为了内存对齐填充的字节数,red zone 等等
    unsigned int size;  /* The size of an object including metadata */
    // slab 中对象的实际大小,不包含填充的字节数
    unsigned int object_size;/* The size of an object without metadata */
    // slab 对象池中的对象在没有被分配之前,我们是不关心对象里边存储的内容的。
    // 内核巧妙的利用对象占用的内存空间存储下一个空闲对象的地址。
    // offset 表示用于存储下一个空闲对象指针的位置距离对象首地址的偏移
    unsigned int offset;    /* Free pointer offset */
    // 表示 cache 中的 slab 大小,包括 slab 所需要申请的页面个数,以及所包含的对象个数
    // 其中低 16 位表示一个 slab 中所包含的对象总数,高 16 位表示一个 slab 所占有的内存页个数。
    struct kmem_cache_order_objects oo;
    // slab 中所能包含对象以及内存页个数的最大值
    struct kmem_cache_order_objects max;
    // 当按照 oo 的尺寸为 slab 申请内存时,如果内存紧张,会采用 min 的尺寸为 slab 申请内存,可以容纳一个对象即可。
    struct kmem_cache_order_objects min;
    // 向伙伴系统申请内存时使用的内存分配标识
    gfp_t allocflags; 
    // slab cache 的引用计数,为 0 时就可以销毁并释放内存回伙伴系统重
    int refcount;   
    // 池化对象的构造函数,用于创建 slab 对象池中的对象
    void (*ctor)(void *);
    // 对象的 object_size 按照 word 字长对齐之后的大小
    unsigned int inuse;  
    // 对象按照指定的 align 进行对齐
    unsigned int align; 
    // slab cache 的名称, 也就是在 slabinfo 命令中 name 那一列
    const char *name;  
};

 slab_flags_t flags 是 slab cache 的管理标志位,用于设置 slab 的一些特性,比如:

  • 当 flags 设置了 SLAB_HWCACHE_ALIGN 时,表示 slab 中的对象需要按照 CPU 硬件高速缓存行 cache line (64 字节) 进行对齐。

  • 当 flags 设置了 SLAB_POISON 时,表示需要在 slab 对象内存中填充特殊字节  0x6b 和  0xa5,表示对象的特定状态。

  • 当 flags 设置了 SLAB_RED_ZONE 时,表示需要在 slab 对象内存周围插入 red zone,防止内存的读写越界。

  • 当 flags 设置了 SLAB_CACHE_DMA 或者 SLAB_CACHE_DMA32 时,表示指定 slab 中的内存来自于哪个内存区域,DMA or DMA32  区域 ?如果没有特殊指定,slab 中的内存一般来自于 NORMAL 直接映射区域。

  • 当 flags 设置了 SLAB_STORE_USER 时,表示需要追踪对象的分配和释放相关信息,这样会在 slab 对象内存区域中额外增加两个 sizeof(struct track) 大小的区域出来,用于存储 slab 对象的分配和释放信息。

slab 的组织架构

内核在对 slab cache 的设计也是一样,也充分考虑了多进程并发访问 slab cache 所带来的同步性能开销,内核在 slab cache 的设计中为每个 cpu 引入了 struct kmem_cache_cpu 结构的 percpu 变量,作为 slab cache 在每个 cpu 中的本地缓存。

这样一来,当进程需要向 slab cache 申请对应的内存块(object)时,首先会直接来到 kmem_cache_cpu 中查看 cpu 本地缓存的 slab,如果本地缓存的 slab 中有空闲对象,那么就直接返回了,整个过程完全没有加锁。而且访问路径特别短,防止了对 CPU 硬件高速缓存 L1Cache 中的 Instruction Cache(指令高速缓存)污染。

 

kmem_cache_cpu 结构中的 tid 是内核为 slab cache 的 cpu 本地缓存结构设置的一个全局唯一的 transaction id ,这个 tid 在 slab cache 分配内存块的时候主要有两个作用:

  1. 内核会将 slab cache 每一次分配内存块或者释放内存块的过程视为一个事物,所以在每次向 slab cache 申请内存块或者将内存块释放回 slab cache 之后,内核都会改变这里的 tid。

  2. tid 也可以简单看做是 cpu 的一个编号,每个 cpu 的 tid 都不相同,可以用来标识区分不同 cpu 的本地缓存 kmem_cache_cpu 结构。

其中 tid 的第二个作用是最主要的,因为进程可能在执行的过程中被更高优先级的进程抢占 cpu (开启 CONFIG_PREEMPT 允许内核抢占)或者被中断,随后进程可能会被内核重新调度到其他 cpu 上执行,这样一来,进程在被抢占之前获取到的 kmem_cache_cpu 就与当前执行进程 cpu 的 kmem_cache_cpu 不一致了。

 

如果开启了 CONFIG_SLUB_CPU_PARTIAL 配置项,那么在 slab cache 的 cpu 本地缓存 kmem_cache_cpu 结构中就会多出一个 partial 列表,partial 列表中存放的都是 partial slub,相当于是 cpu 缓存的备用选择.

当 kmem_cache_cpu->page (被本地 cpu 所缓存的 slab)中的对象已经全部分配出去之后,内核会到 partial 列表中查找一个 partial slab 出来,并从这个 partial slab 中分配一个对象出来,最后将 kmem_cache_cpu->page 指向这个 partial slab,作为新的 cpu 本地缓存 slab。这样一来,下次分配对象的时候,就可以直接从 cpu 本地缓存中获取了。

 

slab cache 的仓库就在 NUMA 节点中,而且在每一个 NUMA 节点中都有一个仓库,当 slab cache 本地 cpu 缓存 kmem_cache_cpu 中没有足够的内存块可供分配时,内核就会来到 NUMA 节点的仓库中拿出 slab 填充到 kmem_cache_cpu 中。

 

 参考文献

细节拉满,80 张图带你一步一步推演 slab 内存池的设计与实现

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

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

相关文章

Windows:prometheus + grafana + wmi_exporter+ 主机信息监控页面 + 支持主机信息告警

1、PrometheusGrafana环境搭建 1.1、Prometheus 下载地址:Download | Prometheus 选择对应的系统的版本下载并解压压缩包,运行程序 本人在window系统上操作 双击 验证 浏览器输入http://localhost:9090/ 点击Staatus>Targets 出现以下即成功 1.…

Unifying Large Language Models and Knowledge Graphs: A Roadmap

5.2 LLM-augmented KG Completion 知识图谱补全(KGC)是指对给定知识图谱中缺失的事实进行推断的任务。与KGE类似,传统的KGC方法主要关注于KG的结构,而没有考虑广泛的文本信息。然而,最近llm的集成使KGC方法能够对文本进行编码或生成事实,以获得更好的KGC性能。这些方法根据…

【数据科学赛】2023大模型应用创新挑战赛 #¥10万 #百度

CompHub 主页增加了“近两周上新的奖金赛”,更加方便查找最新比赛,欢迎访问和反馈! 以下内容摘自比赛主页(点击文末阅读原文进入) Part1赛题介绍 题目 2023大模型应用创新挑战赛 举办平台 Baidu AI Studio 主办方…

git介绍和使用

目录 一、git概述 1、简介 2、下载安装 二、git代码托管服务 1、常用的 Git 代码托管服务 2、使用码云代码托管服务 三、git常用命令 1、git全局设置 2、获取git仓库 3、工作区、暂存区、版本库 概念 4、Git工作区中文件的状态 5、本地仓库操作 6、远程仓库操作 …

Jetson Nano介绍

1. bo1公版介绍 Jetson NanoBO1公版的实物图如下图所示。其中1是TF卡接口,可以进行系统镜像烧写;2是40PIN GPIO扩展接口;3是用来传输数据或使用电源供电的Micro USB接口;4是千兆以太网口;5是USB3.0接口;6是…

CentOS Linux的替代品(五)_BigCloud Enterprise Linux for Euler 21.10 U 3基础安装教程

文章目录 CentOS Linux的替代品(五)_BigCloud Enterprise Linux for Euler 21.10 U 3基础安装教程一、BC-Linux简介二、BigCloud Enterprise Linux for Euler 21.10U3基础安装2.1 下载地址2.2 安装过程 三、简单使用3.1 关闭selinux3.1.1 临时关闭selinu…

QT学习笔记:QT的信号与槽

一、信号与槽(Signal & Slot)的基本概念 1、了解信号与槽 (1)信号(Signal):在特定情况下被发射的事件,例如PushButton 最常见的信号就是鼠标单击时发射的 clicked() 信号&…

Pytest测试框架快速搭建

一、介绍 pytest是一个非常成熟的Python测试框架,能够支持简单的单元测试和复杂的功能测试,还可以用来做selenium/appnium等自动化测试、接口自动化测试(pytestrequests);pytest具有很多第三方插件,并且可…

springboot整合腾讯企业邮箱/企业微信邮箱

springboot整合腾讯企业邮箱/企业微信邮箱 登录后点击“客户端设置” 2.勾选红框中选项并保存 3.点击微信绑定并绑定微信 4.绑定微信后点击生成新密码并保存(复制生成的密码后面会使用) 5.将配置复制到代码中 <dependency><groupId>com.sun

凤凰架构阅读记录-三(分布式算法)

1.Paxos 世界上只有一种共识协议&#xff0c;就是 Paxos&#xff0c;其他所有共识算法都是 Paxos 的退化版本。 算法流程: 1.提案节点&#xff1a;称为 Proposer&#xff0c;提出对某个值进行设置操作的节点&#xff0c;设置值这个行为就被称之为提案&#xff08;Proposal&a…

【海量数据挖掘/数据分析】 之 K-NN 分类(K-NN、K-NN实例、准确率评估方法、准确率、召回率)

【海量数据挖掘/数据分析】 之 K-NN 分类(K-NN、K-NN实例、准确率评估方法、准确率、召回率) 目录 【海量数据挖掘/数据分析】 之 K-NN 分类(K-NN、K-NN实例、准确率评估方法、准确率、召回率) 一、 K-NN 简介 二、K-NN 分类 三、K-NN 分类实例 1、1-NN 分类 : 此时 A 类别…

leetcode 859. Buddy Strings(同伴字符串)

给出字符串s 和 goal, 判断交换 s 中的一对字母是否能得到goal. 必须是不同位置字母的交换。 思路&#xff1a; 先考虑边界情况&#xff0c; s 和 goal 的长度不相等时&#xff0c;s 怎么交换都不会得到goal. s 的长度 < 2时&#xff0c;没有可交换对象&#xff0c;肯定是…

【力扣】94、二叉树的中序遍历

94、二叉树的中序遍历 注&#xff1a;二叉树的中序遍历&#xff1a;左根右&#xff1b; // 非递归&#xff1a; var inorderTraversal function (root) {const arr [];//创建新数组&#xff1b;const stack [];//创建一个栈道&#xff1b;let o root;while( stack.length |…

办公实现降本增效,可借助微服务开源平台的力量!

在如今的快节奏发展时代&#xff0c;低代码开发平台拥有了一个广阔的发展天地&#xff0c;逐渐在通信业、医疗、物流、高校等众多大中型企业中获得一个立足之地&#xff0c;是助力企业提质、降本、增效的得力助手。微服务开源平台可以让企业实现这一目的&#xff0c;那么对于企…

AMBER分子动力学能量优化与分析、结合自由能计算专题

AMBER分子动力学能量优化与分析、结合自由能计算专题 第一天 时间:AM9:00~9:50 内容:六.分子动力学入门理/论 教学目标&#xff1a;了解本方向内容、理论基础、研究意义。 主要知识点:4分子力学简介 4.1分子力学的基本假设 4.2分子力学的主要形式 5分子力场 5.1分子力场的简…

passware kit forensic使用

一、从外部注册表文件提取密码 适用于不联网的情况下&#xff0c;例如2023盘古石杯的NAS取证 找到Windows/System32/config并在本地打开 将路径填充到config folder中 跑出来了John电脑对应的密码是paofen&#xff0c;NAS的密码是P88w0rd 后续遇见再补

Linux - 那些年测试服务器带宽的 3 种方式

方式一 speedtest-cli wget -O speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.pychmod x speedtest-cliorcurl -Lo speedtest-cli https://raw.githubusercontent.com/sivel/speedtest-cli/master/speedtest.pychmod x speedtest-c…

【Linux】基础开发工具——yum篇

目录 &#x1f4d6;Linux下安装软件的三种方案&#x1f4f0;源代码安装&#x1f4f0;rpm安装&#x1f4f0;yum安装 &#x1f4d6;Linux软件包管理器yum&#x1f4f0;rz、sz&#x1f4f0;查看软件包&#x1f4f0;软件安装&#x1f4f0;软件删除 &#x1f4d6;yum源问题&#x1f…

Hadoop:DataNode启动失败:Initialization failed for Block pool

一、问题发现 ​在启动Hadoop集群后&#xff0c;发现原本应该是启动三台机子的数据节点&#xff0c;结果只有一台node2成功启动&#xff0c;另外两台&#xff08;node1、node3&#xff09;并没有启动&#xff0c;如下两图所示&#xff1a; 问题描述&#xff1a; ​确认配置信…

day3 QT对话框

目录 QDialog 标准对话框&#xff08;Standard Dialogs&#xff09;&#xff1a; 内置对话框&#xff08;Built-in Dialogs&#xff09;&#xff1a; 自定义对话框&#xff08;Custom Dialogs&#xff09; 示例 myqdialog.h widget.h myqdialog.cpp widget.cpp QDialog Q…