Slub(Slab)

定义

用于分配小于一页(4K)的内存,减少内部碎片。Linux 6.x目前slub最大可以分配8K(也就是2页)的内存。

slab、slub、slob

  • slub:是对slab分配器的改进,目前Linux里使用的主流。
  • slob:是一种轻量级的内存分配器,通常用于嵌入式设备。

由于slub分配器使用最为广泛,重点讨论slub分配器。

slub最小分配单元:字节(object:通常大于8字节)

slub从buddy system批发进货,然后再拆分零售给消费者(使用kmalloc的用户)

数据结构

主要由:kmem_cache,kmem_cache_cpu,kmem_cache_node,slab,object 组成。

  1. slab_caches

  • 所有被Slub的管理的内存叫slab caches
  • 一个slub caches下分为N个slab cache
  • slab cache包含N个slab
  • 每个slab又被划分成N个object。object就是内存分配的对象,也是最小管理单元。

Slab的链表分三种:

  • full:slabs_full,完全分配的slab
  • partial:slabs_partial,部分分配的slab
  • slab:slabs_empty,空slab,或者没有对象被分配。

PS: 在slub的实现中,只剩下了partial

slab caches通过一个叫slab_caches的变量表示,是一个链表,挂载着所有的slab cache。

定义在mm/slab_common.c中:

1. kmem_cache结构体

用于描述一个slub cache,包括slab cache的名称、每个element的size、slab cache的size、etc。

定义于mm/slab.h中:

  • name:每个kmem_cache都有自己的名字,可以通过/proc/slabinfo查看。name通过kmem_cache_create函数创建kmem_cache指定。

  • list:通过list于其他的kmem_cache连接起来,list head就是slab_caches。
  • cpu_slab: 为每个CPU core分配独立的kmem_cache_cpu结构, 每个core通过自己的kmem_cache_cpu直接从本地缓存分配内存对象。
  • min_partial :二级回收链表(node[x]->partial)上page数量超过min_partial时,将会把二级回收链表超过的部分,释放回伙伴系统
  • object_size:object是slub内存的最小单元,也是管理的基本对象。
  • node:是为每个NUMA node保存slab信息struct kmem_cache_node。相当于拥有一个全局的slabs列表,尚未绑定到任何CPU,但是也仍然属于cache,也会包含已经分配的objects。
  • cpu_partial: 一级回收链表(cpu_slab->partial)上object超过cpu_partial时,将会把一级回收链表上所有page移动到二级回收链表上

PS:

一级回收链表:s->cpu_slab->partial

二级(回收链表:s->node[x]->partial

 

2. kmem_cache_cpu结构体

  • void **freelist:指向当前cpu slab的空闲object。
  • tid:记录最后一个分配/释放对象的CPU id,用于判断是否需要填充空闲对象链表。
  • slab:指向正在分配的 slab 页。
  • partial:指向所有分配了一部分对象但未完全分配的 slab。

3. kmem_cache_node结构体

是numa node内存节点空闲slab链表。

  • nr_partial:当前 NUMA node中 partial slab 链表中 slab 的数量。
  • partial:部分已分配的 slab ,包含所有分配了一部分对象但未完全分配的 slab。

4. struct slab

一个slab被一个struct slab结构体所描述,其实就是一个struct page。

在Linux内核5.17版本中,struct slab被引入,目的是将slab相关的字段从struct page中分离出来。在struct slab作为struct page的一个overlay,共享同一块内存,但隐藏了struct page的细节,这样slab分配器只需要处理自己的结构。

一个slab cache可以理解为多个slab的集合,在实现上一个slab有一个或多个连续的物理页组成(通常只有一页),将slab根据size大小划分成一个个object,object就是内存分配的对象。

  • slab_cache:指向这个slab属于的slab cache。
  • freelist:指向slab中第一个空闲object。
  • inuse:slab中已经分配的object数量。
  • objects:slab中可以容纳的object数量。

5. object

slub分配器的最小单元。一个object由red left pad,object内存区域,red zone,freepinter,track,padding等部分组成。

  • object_size: 真正的object内存区域。
  • freelist:指向第一个空闲的object。
  • freepointer:指向下一个空闲的object。
  • red zone: Linux内核SLAB/SLUB内存中的一种调试和保护机制,用于检测内存越界访问。在object内存区域前后插入red zone,前:red left pad, 后:red zone。如果写入或读取了red zone,内核会触发警告。内核(5.x+)通常通过 CONFIG_SLUB_DEBUG 统一控制红区功能。

总结

函数实现

内核提供4个函数来使用slub:

  • kmem_cache_create:创建kmem_cache,成功后返回一个kmem_cache的指针。
  • kmem_cache_destroy:销毁kmem_cache。
  • kmem_cache_alloc:从slab申请一个object。调用成功后,返回object的虚拟地址。
  • kmem_cache_free:释放object到slab。

创建slub cache

1. kmem_cache_init

Slub初始化,只有slub系统已经完成初始化后,才能使用kmalloc。

kmem_cache_init在内核初始化阶段(start_kernel)、伙伴系统启用之后被调用。

2. kmem_cache_create

定义在include/linux/slab.h:

2.1 create_cache

定义在mm/slab_common.c:

(1)kmem_cache也有一个专门的slab,从这个slab申请一块kmem_cache结构体内存。

(2)do_kmem_cache_create:初始化kmem_cache。

(3)把创建好的kmem_cache添加到slab_caches(全局总链表)中。

3. do_kmem_cache_create

初始化分配好的kmem_cache,具体如下:

  • 先调用calculate_sizes来计算object中数据的顺序和分布,并计算每一个slab页能容下多少个object。
  • 创建并初始化kmem_cache_cpu和kmem_cache_node。

从slub分配内存

分配策略

申请object对象有两种情况:快速路径和慢速路径

(1)快速路径:如果slab(freelist或partial slab)还有空闲的object对象,则直接从cpu slab申请object。

(2)慢速路径:如果cpu slab没有空闲的object,则需要先检查node slab是否有空闲object,如果有则从slab链表摘除slab插入cpu freelist,如果没有则从伙伴系统申请slab并插入cpu freelist。

总结:object分配优先级:cpu freelist slab>cpu partial slab > node partial slab > buddy system

 

函数实现

Linux中从slub分配一个object有两个函数,定义在include/linux/slab.h中:

  • kmem_cache_alloc分配一个object
  • kmem_cache_alloc_node:从一个指定的NUMA node中分配一个object

 

这两个函数最终都会调用到slab_alloc_node, kmalloc最终也是调用到这个函数。

 

1. slab_alloc_node

定义在mm/slub.c中

(1)slab 缓存分配内存之前执行一些必要的检查或操作

(2)kfence是内核引入的一种内存错误检测工具

(3)真正的slub分配函数:__slab_alloc_node

2. __slab_alloc_node

精简函数如下:

(1)从kmem_cache_cpu->free_list里取出object。

(2)如果object为NULL,就走慢速路径分配。

(3)如果object不为空,就把当前这个object分配出去,并把freelist指向下一个可用的object。

3. 快速路径分配

(1)通过object = cpu_slab->freelist,将object取出。

(2)通过get_freepointer_safe寻找下一个可用的object。

kmem_cache->offset指向的就是object中的freeponiter,freepoint中装的就是下一个可用object的地址。将freepointer中的内容copy给p以后,再经过其他加工然后返回。

(3)通过__update_cpu_freelist_fast把新的object更新到cpu_slab->freelist中。

(4)通过prefetch_freepointer,把新object的freepointer指向的地址,预取到cache中。为的是减少cache miss,提高性能。

4. 慢速路径分配:__slab_alloc

__slab_alloc调用___slab_alloc,定义在mm/slub.c 中。

___slab_alloc做的事情可以分为两步:

1. 先检查cpu slab的partital中有没有可用的slab页,如果有就分配。

2. 如果cpu slab的partital为空,就从node slab的partital中找。

通过get_partial从node slab拿,默认如果本node没有,没有经过特殊设置不会从别的node拿。

3. 如果node slab的partital为空,就调用new_slab从buddy system申请。

new_slab的调用栈,一直调用到buddy system的分配函数:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值