• slab机制总结篇


    一:

    slab是为了解决内部碎片提出的,还是外部碎片?

    为了解决内部碎片。

    内部碎片的产生:因为所有的内存分配必须起始于可被 4、8 或 16 整除(视处理器体系结构而定)的地址或者因为MMU的分页机制的限制,决定内存分配算法仅能把预定大小的内存块分配给客户。假设当某个客户请求一个 43 字节的内存块时,因为没有适合大小的内存,所以它可能会获得 44字节、48字节等稍大一点的字节,因此由所需大小四舍五入而产生的多余空间就叫内部碎片。

    外部碎片的产生: 频繁的分配与回收物理页面会导致大量的、连续且小的页面块夹杂在已分配的页面中间,就会产生外部碎片。假设有一块一共有100个单位的连续空闲内存空间,范围是0~99。如果你从中申请一块内存,如10个单位,那么申请出来的内存块就为0~9区间。这时候你继续申请一块内存,比如说5个单位大,第二块得到的内存块就应该为10~14区间。如果你把第一块内存块释放,然后再申请一块大于10个单位的内存块,比如说20个单位。因为刚被释放的内存块不能满足新的请求,所以只能从15开始分配出20个单位的内存块。现在整个内存空间的状态是0~9空闲,10~14被占用,15~24被占用,25~99空闲。其中0~9就是一个内存碎片了。如果10~14一直被占用,而以后申请的空间都大于10个单位,那么0~9就永远用不上了,变成外部碎片。

    二:

    slab算法的核心思想是什么?

    slab的核心思想是以对象的观点来管理内存。

    内核对其对象的使用具有以下特殊性:

    1. 内核使用的对象种类繁多,应该采用一种统一的高效管理方法。
    2. 内核对某些对象(如 task_struct)的使用是非常频繁的,所以用户进程堆管理常用的基于搜索的分配算法比如First-Fit(在堆中搜索到的第一个满足请求的内存块)和 Best-Fit(使用堆中满足请求的最合适的内存块)并不直接适用,而应该采用某种缓冲区的机制。
    3. 内核对象中相当一部分成员需要某些特殊的初始化(例如队列头部)而并非简单地清成全 0。如果能充分重用已被释放的对象使得下次分配时无需初始化,那么可以提高内核的运行效率。
    4. 分配器对内核对象缓冲区的组织和管理必须充分考虑对硬件高速缓存的影响。
    5. 随着共享内存的多处理器系统的普及,多处理器同时分配某种类型对象的现象时常发生,因此分配器应该尽量避免处理器间同步的开销,应采用某种 Lock-Free 的算法。

    所以,这就需要slab机制以对象的观点来解决小内存问题。

    三:有关slab的着色问题

    着色与硬件cache有关,这就牵扯到cache和主存的工作结构:

    1. 全相连
      • 任一主存块能映射到任意缓存行(主存块的大小等于缓存行的大小。
      • 优点:灵活,不易产生冲突
      • 缺点:比较电路难于实现,且效率低,速度慢
    2. 直接映射
      • 某一主存块只能映射到特定的缓存行
      • 优点:硬件简单,成本低
      • 缺点:容易产生冲突,易产生缓存“颠簸”,不能有效利用cache空间
    3. 组相联
      • 组间直接映射,组内全相联映射
      • 优点:结合上面两种的优点
        • 因为组内行数较少,比较器容易实现
        • 组内又有灵活性,冲突大大减小

    由上可知,slab着色对于直接映射和组相连映射的工作结构效率帮助较大,而全项链结构本身冲突就比较小,那么着色的帮助是很小的。在全相连工作结构中,使用着色无疑是一个巨大的内存浪费。

    slab算法的缺点

    随着大规模多处理器系统和NUMA系统的广泛应用,slab分配器逐渐暴露出自身严重的不足:

    1. 较多复杂的队列管理。在slab分配器中存在众多的队列,例如针对处理器的本地缓存队列,slab中空闲队列,每个slab处于一个特定状态的队列之中。所以,管理太费劲了。
    2. slab管理数据和队列的存储开销比较大。每个slab需要一个struct slab数据结构和一个管理者kmem_bufctl_t型的数组。当对象体积较小时,该数组将造成较大的开销(比如对象大小为32字节时,将浪费1/8空间)。为了使得对象在硬件告诉缓存中对齐和使用着色策略,还必须浪费额外的内存。同时,缓冲区针对节点和处理器的队列也会浪费不少内存。测试表明在一个1000节点/处理器的大规模NUMA系统中,数GB内存被用来维护队列和对象引用。
    3. 缓冲区回收比较复杂。
    4. 对NUMA的支持非常复杂。slab对NUMA的支持基于物理页框分配器,无法细粒度的使用对象,因此不能保证处理器级的缓存来自同一节点(这个我暂时不太懂)。
    5. 冗余的partial队列。slab分配器针对每个节点都有一个partial队列,随着时间流逝,将有大量的partial slab产生,不利于内存的合理使用。
    6. 性能调优比较困难。针对每个slab可以调整的参数比较复杂,而且分配处理器本地缓存时,不得不使用自旋锁。
    7. 调试功能比较难于使用。

    为了解决以上slab分配器的不足,引入新的解决方案,slub分配器。slub分配器的特点是简化设计理念,同时保留slab分配器的基本思想:每个缓冲区有多个slab组成,每个slab包含固定数目的对象。slub分配器简化了kmem_cache,slab等相关的管理结构,摈弃了slab分配器中的众多队列概念,并针对多处理器、NUMA系统进行优化,从而提高了性能和可扩展性并降低了内存的浪费。并且,为了保证内核其他模块能无缝迁移到slub分配器,API接口函数与slab保持一致。

    缺点简单说就是:

    1. 缓存队列管理复杂;
    2. 管理数据存储开销大;
    3. 对NUMA支持复杂;
    4. 调试调优困难;
    5. 摒弃了效果不太明显的slab着色机制;

    过些天就开始剖析slub分配器,不过由于有slab的基础,那个已经是小菜了。

  • 相关阅读:
    vue项目实战:原生百度地图api的使用
    vue项目实战:页面公共组件的全局注册动态引入的考虑
    celery+django的定时任务设置
    celery.exceptions.NotRegistered: 'tasks.add' + celery delay 坑 + python manage.py check 查看状态
    from __future__ import absolute_import的作用
    Django与celery集成:异步任务原理和过程
    python -m pip install --upgrade pip --default-timeout=100 -i http://pypi.douban.com/simple/ --trusted-host pypi.douban.com
    Django 的安全
    django集成sentry实现日志上报和钉钉告警
    TypeScript(21): 装饰器
  • 原文地址:https://www.cnblogs.com/libertylife/p/9577249.html
Copyright © 2020-2023  润新知