操作系统实战 45 讲
彭东
网名 LMOS,Intel 傲腾项目关键开发者
65203 人已学习
新⼈⾸单¥68
登录后,你可以任选4讲全文学习
课程目录
已完结/共 60 讲
尝尝鲜:从一个Hello到另一个Hello (2讲)
特别放送 (1讲)
操作系统实战 45 讲
15
15
1.0x
00:00/00:00
登录|注册

23 | 瞧一瞧Linux:SLAB如何分配内存?

cache_grow_end
cache_grow_begin
get_first_slab
cache_grow_end
cache_grow_begin
get_first_slab
cache_alloc_refill
cpu_cache_get
cache_alloc_refill
cpu_cache_get
create_boot_cache
kmem_cache_zalloc
kmalloc_caches
____cache_alloc
____cache_alloc
create_kmalloc_caches
new_kmalloc_cache
create_kmalloc_cache
kmalloc_slab
slab_alloc
kmem_cache_init
init_list
slab_alloc
__do_kmalloc
__do_kmalloc
kmem_cache_node
array_cache
kmalloc函数
kmalloc函数
kmem_cache结构
SLAB分配接口
SLAB分配接口
管理kmem_cache
SLAB分配对象的过程
SLAB对象
思考题
SLAB分配器
瞧一瞧Linux:SLAB如何分配内存?

该思维导图由 AI 生成,仅供参考

你好,我是 LMOS。
上节课我们学习了伙伴系统,了解了它是怎样管理物理内存页面的。那么你自然会想到这个问题:Linux 系统中,比页更小的内存对象要怎样分配呢?
带着这个问题,我们来一起看看 SLAB 分配器的原理和实现。在学习过程中,你也可以对照一下我们 Cosmos 的内存管理组件,看看两者的内存管理有哪些异同。

SLAB

与 Cosmos 物理内存页面管理器一样,Linux 中的伙伴系统是以页面为最小单位分配的,现实更多要以内核对象为单位分配内存,其实更具体一点说,就是根据内核对象的实例变量大小来申请和释放内存空间,这些数据结构实例变量的大小通常从几十字节到几百字节不等,远远小于一个页面的大小。
如果一个几十字节大小的数据结构实例变量,就要为此分配一个页面,这无疑是对宝贵物理内存的一种巨大浪费,因此一个更好的技术方案应运而生,就是 Slab 分配器(由 Sun 公司的雇员 Jeff Bonwick 在 Solaris 2.4 中设计并实现)。
由于作者公开了实现方法,后来被 Linux 所借鉴,用于实现内核中更小粒度的内存分配。看看吧,你以为 Linux 很强大,真的强大吗?不过是站在巨人的肩膀上飞翔的。

走进 SLAB 对象

何为 SLAB 对象?在 SLAB 分配器中,它把一个内存页面或者一组连续的内存页面,划分成大小相同的块,其中这一个小的内存块就是 SLAB 对象,但是这一组连续的内存页面中不只是 SLAB 对象,还有 SLAB 管理头和着色区。
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

SLAB分配器是Linux内核中用于管理更小粒度内存分配的技术。它将内存页面划分成大小相同的块,称为SLAB对象,以避免对宝贵物理内存的浪费。在SLAB分配对象的过程中,根据请求分配对象的大小,查找对应的kmem_cache结构,获取array_cache结构,然后分配对象。如果没有空闲对象,就需要在kmem_cache对应的kmem_cache_node结构中查找有空闲对象的kmem_cache,最后可能需要分配内存页面新增kmem_cache结构。SLAB分配器的实现方法为Linux系统带来了更高效的内存管理方式,使得系统性能得到提升。 文章详细介绍了SLAB分配接口、如何查找kmem_cache结构以及对象的分配过程。通过分析代码,读者可以了解SLAB分配器的内部工作原理,包括对象的分配和内存页面的管理。这些内容对于理解Linux内核中的内存管理机制具有重要意义。 总的来说,本文通过代码分析的方式深入探讨了SLAB分配器的实现细节,对于希望深入了解Linux内核内存管理的读者来说,是一篇具有很高参考价值的文章。 SLAB分配器的高效管理方式为Linux系统带来了性能提升,而文章通过详细介绍SLAB分配接口和内部工作原理,使读者能够深入了解其实现细节,为理解Linux内核内存管理机制提供了重要参考。 通过本文的阅读,读者可以对SLAB分配器有更深入的了解,从而为深入研究Linux内核内存管理提供了重要的基础知识。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《操作系统实战 45 讲》
新⼈⾸单¥68
立即购买
登录 后留言

全部留言(16)

  • 最新
  • 精选
  • 搬铁少年ai
    置顶
    回答有的同学关于为什么是196大小的问题 这里196大小的对象,应该是专门针对256B以下小内存进行的优化,正常情况支持的对象大小都是2的n次方,2的七次方是128,8次方就是256了。所以这里在不违反缓存对其的前提下,单独支持了196大小的对象。 如果cache line 是32的话,192/32=6,也是缓存对其的,那么如果申请的内存在129到192之间时,就不必去分配256大小的对象,而是可以分配192大小的对象,可以在满足缓存对齐的前提下节省空间。 除了192,另外在2的6次方和7次方之间,也特殊支持了96b大小的对象,同样是类似的原理。 理论上能够背cache line大小整除的都可以特殊支持,只不过256以上的对象可能不常见,slab申请了特殊大小的对象却没有人用,反倒是一种浪费

    作者回复: 666

    2021-11-05
    2
    4
  • neohope
    一、数据结构 系统有一个全局kmem_cache_node数组,每一个kmem_cache_node结构,对应一个内存节点 kmem_cache_node结构,用三个链表管理内存节点的全部kmem_cache【slab管理结构】,包括: slabs_partial,对象部分已分配的kmem_cache结构; slabs_full,对象全部已分配的kmem_cache结构; slabs_free ,对象全部空闲kmem_cache结构; kmem_cache结构,slab管理头,包括: array_cache,每个CPU一个,用于管理空闲对象。 array_cache的entry数组,用于管理这些空闲对象,出入遵循LIFO原则; num,表示对象个数; gfporder,表示页面的大小 (2^n); colour,表示着色区大小。着色区,主要利用SLAB划分对象剩余的空间,让SLAB前面的几个对象,根据cache line大小进行偏移,以缓解缓存过热的问题,防止Cache地址争用,防止引起Cache抖动; 此外,全局有一个slab_caches链表中,记录了系统中全部的slab 二、初始化 全局有一个kmem_cache结构,kmem_cache_boot,用于初始化 全局有一个kmem_cache_node数组结构init_kmem_cache_node,用于初始化 x86_64_start_kernel->x86_64_start_reservations->start_kernel->mm_init -> kmem_cache_init 1、将变量kmem_cache指向静态变量kmem_cache_boot 2、初始化全局的init_kmem_cache_node结构 3、调用create_boot_cache,初始化kmem_cache_boot结构 4、将kmem_cache_boot其加入全局slab_caches链表中 5、调用create_kmalloc_cache,建立第一个kmem_cache,供kmalloc函数使用 6、调用init_list函数,将静态init_kmem_cache_node,替换为用kmalloc生成的kmem_cache_node 7、 调用create_kmalloc_caches,创建并初始化了全部 kmalloc_caches中的kmem_cache 路径为:kmem_cache_init->create_kmalloc_caches-> new_kmalloc_cache-> create_kmalloc_cache 三、对象分配 kmalloc->__kmalloc->__do_kmalloc ->kmalloc_slab 从kmalloc_caches中,根据类型和大小,找到对应的 kmem_cache ->slab_alloc->__do_cache_alloc->____cache_alloc 1、第一级分配,如果array_cache.entry中有空闲对象,直接分配 2、如果一级分配失败,调用cache_alloc_refill,进行第二级分配 ->->cache_alloc_refill从全局的slab中进行refill 1、如果没有空闲对象,而且shared arry没有共享对象可用,需要扩容 2、如果shared arry有空闲对象,直接分配,否则继续 3、尝试从kmem_cache_node结构中其它kmem_cache获取slab页面 4、如果都失败了就扩容 如果一、二级分配都失败了,那就扩容,并进行第三级分配: 1、再次尝试在不扩容情况下,分配新的kmem_cache并初始化,如果成功就返回 2、调用cache_grow_begin 函数,找伙伴系统分配新的内存页面,找第一个 kmem_cache 分配新的对象,来存放 kmem_cache 结构的实例变量,并进行必要的初始化 3、调用 cache_grow_end 函数,把这页面挂载到 kmem_cache_node 结构的空闲链表中 4、返回一个空闲对象 四、对象回收 kfree->__cache_free->___cache_free->__free_one 将对象清空后,还给了CPU的对应的array_cache

    作者回复: 老哥 每次都 总结 相当到位

    2021-07-07
    2
    14
  • tony
    既然已经有了slab分配机制,为什么在用户态还有ptmalloc以及tcmalloc?它们侧重点有什么不一样

    作者回复: 不用切换到内核态 增强性能

    2022-11-21归属地:湖北
    2
  • 西门吹牛
    之前学 netty,netty 中用到了伙伴算法实现内存分配与释放,说下 netty 中的实现吧: 首先会预申请一大块内存 PoolArena,内部由 6 个 PoolChunkList,和俩个 PoolSubpage[] ● 6 个 PoolChunkList:分别是 qInit、q000、q025、q050、q075、q100 ○ Netty 根据 PoolChunk 的使用率,将他们分别放入对应的 PoolChunkList 中,目的减少内存碎片 ○ 每个 PoolChunk 默认 16MB,每个 PoolChunk 有划分为 2048 个 Subpage,每个 Subpage 8KB,16MB/2048 = 8KB ○ PoolChunk 划分的 2048 个 8KB 的 Subpage 构成满二叉树 ● PoolSubpage[]:用于分配小于 8KB 的内存 ○ PoolSubpage[] 中的元素是指向 8KB 大小的 Subpage 地址,同时又把 8KB 的 subpage 分割成大小相等的段,比如 32B,64B...... 分配大于 8 KB 的内存,直接走 PoolChunk 对应满二叉树,这样们更好的避免内存碎片,比如: ○ 先分配 8KB:需要一个 Page ,满二叉树最下一层满足要求,故分配这层的第一个节点page0 ○ 在分配 16KB:需要两个Page ,满二叉树倒数第二层满足要求,因为这层的下一层的第一个节点page0已被分配,所以选这层第二个节点,就是相当分配 page2和page3 ○ 在分配 8KB:需要一个 Page ,满二叉树最下一层满足要求,page0 以占用,往后page1可用,直接分配 经过这样分配,最终分配的是page0、page1、page2、page3 刚好这四个页是连续的。 对于小于 8KB 的分配:比如32B: ○ 定位到 PoolSubpage[] 中的元素,看有没有值,没有代表之前没有分配过,执行分配,有值代表之前分配过 32B 的空间 ○ 如果没有分配过,那么先取一个 8KB的page,将数组中对应的元素指向该page ○ 在将 8KB 的page 按 32B 划分成相等的段,然后取划分好的第一个 32B 的段拿出使用,并把该段标记为占用 ○ 等下次在分配 32B 的时候,先定位到数组对应的元素,有值代表之前分配过 32B 的空间,那么该元素指向的 page 已经是被按 32B 划分好的相同的小段 ○ 那么就可以直接从划分好的小段中,依次遍历,取出没有使用的那个 32B 的段来分配 也就是说,第一次分配小于 8KB (比如32B)的内存的时候,已经在内存中分配好了若干相同的32B 的段了,后续可以直接取用第一次分配好的 当然,其中还有很多细节,比如是否池化,内存释放之后,是直接归还,还是先缓存起来,下次在用,多线程申请的时候,怎么避免竞争等问题

    作者回复: 哇 牛牛牛

    2022-07-06
    2
  • 艾恩凝
    打卡,看了记,记了忘,忘了看,内存的相关学习,我在路上

    作者回复: 哈哈 加油

    2022-04-22
    1
  • 搬铁少年ai
    请教老师,为什么有的资料说struct page就是slab,您这里说kmem_cache是描述slab,有点糊涂。

    作者回复: SLOB

    2021-11-03
    1
  • 搬铁少年ai
    请教老师,我看kmem_cache源码里的node是一个指向kmem_cache_node的指针数组,您这里给的是一个指针,如果是指针我是理解的,但是如果是指针数组,我不明白为什么需要多个node管理kmem_cache(slab头)

    作者回复: 因为cache对象大小不同

    2021-11-02
    1
  • Samaritan.
    “在 Linux 中,SLAB 管理头用 kmem_cache 结构来表示,代码如下”,请问一下,作者引用的是linux的哪个内核版本的代码呀?

    作者回复: 5.10.13

    2021-09-22
    1
  • 青玉白露
    其实在 kmalloc_slab 函数已经写明了,最大是192,单位应该是B吧?

    作者回复: 对的

    2021-07-13
    1
  • 朱熙
    linux内核包括三种小对象管理方式,slab,slub和slob,其中slob效率较低用于嵌入式等,linux默认使用slub

    作者回复: 是的

    2021-07-03
    1
收起评论
显示
设置
留言
16
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部