详解slab机制(1) slab是什么
目前有很多讲slab的文章,要么是纯讲原理画一堆图结合源码不深导致理解困难,要么是纯代码注释导致理解更困难,我在猛攻了一周时间后,细致总结一下slab,争取从原理到源码都能细致的理解到并立刻达到清楚的使用。
一、slab分配器概述:
有了伙伴系统buddy,我们可以以页为单位获取连续的物理内存了,即4K为单位的获取,但如果需要频繁的获取/释放并不大的连续物理内存怎么办,如几十字节几百字节的获取/释放,这样的话用buddy就不太合适了,这就引出了slab。
比如我需要一个100字节的连续物理内存,那么内核slab分配器会给我提供一个相应大小的连续物理内存单元,为128字节大小(不会是整好100字节,而是这个档的一个对齐值,如100字节对应128字节,30字节对应32字节,60字节对应64字节),这个物理内存实际上还是从伙伴系统获取的物理页;当我不再需要这个内存时应该释放它,释放它并非把它归还给伙伴系统,而是归还给slab分配器,这样等再需要获取时无需再从伙伴系统申请,这也就是为什么slab分配器往往会把最近释放的内存(即所谓“热”)分配给申请者,这样效率是比较高的。
二、创建一个slab:
2.1、什么叫创建slab:
上面举了申请100字节连续物理内存的例子,还提到了实际分配的是128字节内存,也就是实际上内核中slab分配器对不同长度内存是分档的,其实这就是slab分配器的一个基本原则,按申请的内存的大小分配相应长度的内存。
同时也说明一个事实,内核中一定应该有这样的按不同长度slab内存单元,也就是说已经创建过这样的内存块,否则申请时怎能根据大小识别应该分配给怎样大小的内存,这可以先参加kmalloc的实现,kmalloc->__do_kmalloc,__do_kmalloc函数中的如下:
static __always_inline void *__do_kmalloc(size_t size, gfp_t flags, void *caller)
{
struct kmem_cache *cachep;
void *ret;
/*找一个合适大小的高速缓存*/
cachep = __find_general_cachep(size, flags);
if (unlikely(ZERO_OR_NULL_PTR(cachep)))
return cachep;
ret = __cache_alloc(cachep, flags, caller);
trace_kmalloc((unsigned long) caller, ret,
size, cachep->buffer_size, flags);
return ret;
}
加深的部分就是说,kmalloc申请的物理内存长度为参数size,它需要先根据这个长度找到相应的长度的缓存,这个缓存的概念是什么马上就要引出先别着急,先看函数__find_general_cachep:
static inline struct kmem_cache *__find_general_cachep(size_t size, gfp_t gfpflags)
{
struct cache_sizes *csizep = malloc_sizes;
#if DEBUG
/* This happens if someone tries to call
* kmem_cache_create(), or __kmalloc(), before
* the generic caches are initialized.
*/
BUG_ON(malloc_sizes[INDEX_AC].cs_cachep == NULL);
#endif
if (!size)
return ZERO_SIZE_PTR;
/*这是本函数唯一有用的地方: 寻找合适大小的cache_sizes*/
while (size > csizep->cs_size)
csizep++;
/*
* Really subtle: The last entry with cs->cs_size==ULONG_MAX
* has cs_{dma,}cachep==NULL. Thus no special case
* for large kmalloc calls required.
*/
#ifdef CONFIG_ZONE_DMA
if (unlikely(gfpflags & GFP_DMA))
return csizep->cs_dmacachep;
#endif
return csizep->cs_cachep;
}
如上面加深的部分所示,这个函数唯一有用的部分就是这里,csizep初始化成全局变量malloc_sizes,根据全局变量malloc_sizes的cs_size成员和size的大小比较,不断后移malloc_sizes,现在就要看看malloc_sizes是怎么回事:
struct cache_sizes malloc_sizes[] = {
#define CACHE(x) { .cs_size = (x) },
#include <linux/kmalloc_sizes.h>
CACHE(ULONG_MAX)
#undef CACHE
};
观察文件linux/kmalloc_sizes.h的情况,篇幅太大这个文件内容就不列了,里面都是一堆的CACHE(X)的宏声明,根据里边的定制宏情况(L1_CACHE_BYTES值为32,KMALLOC_MAX_SIZE值为4194304),一共声明了CACHE(32)、CACHE(64)、CACHE(96)、CACHE(128)、CACHE(192)、CACHE(256)、CACHE(512)、CACHE(1024)、CACHE(2048)、CACHE(4096)、CACHE(8192)、CACHE(16384)、CACHE(32768)、CACHE(65536)、CACHE(131072)、CACHE(262144)、CACHE(524288)、CACHE(1048576)、CACHE(2097152)、CACHE(4194304)和最后的CACHE(0xffffffff)共计21个CACHE(X)的宏声明,结合结构类型struct cache_sizes,对于arm它实际上有两个成员:
struct cache_sizes {
size_t cs_size;
struct kmem_cache *cs_cachep;
#ifdef CONFIG_ZONE_DMA
struct kmem_cache *cs_dmacachep;
#endif
};
除X86以外基本都没有DMA必须在物理内存前16MB的限制,所以包括arm的很多体系结构都没有CONFIG_ZONE_DMA,所以本结构实际上是两个成员cs_size和cs_cachep,那么这里就比较清晰了,全局变量malloc_sizes共有21个成员,每个成员都定义了cs_size值,从32到4194304加上0xffffffff,cs_cachep都为NULL;其实这些值就是slab分配器的一个个按长度的分档;
回到函数__find_general_cachep,已经很清晰了,全局变量malloc_sizes的第0个成员开始,当申请的内存长度比该成员的档次值cs_size大,就换下一个成员,直到比它小为止,仍然如申请100字节的例子,在96字节的分档时还比申请长度小,在128字节的分档时就可以满足了,这就是为什么说申请100字节实际获取到的是128字节的内存单元的原因。
回到函数__do_kmalloc,接下来调用的是__cache_alloc,将按照前面确定的内存分档值给申请者分配一个相应值的内存,这说明,内核有能力给分配这样的内存单元;
内核为什么有能力创建这样的内存单元?slab分配器并非一开始就能智能的根据内存分档值分配相应长度的内存的,它需要先创建一个这样的“规则”式的东西,之后才可以根据这个“规则”分配相应长度的内存,看看前面的结构struct cache_sizes的定义,里边的成员cs_cachep,它的结构类型是struct kmem_cache *,这个结构也是同样是刚才提到的缓存的概念,每种长度的slab分配都得通过它对应的cache分配,换句话说就是每种cache对应一种长度的slab分配,这里顺便能看看slab分配接口,一个是函数kmalloc一个是函数kmem_cache_alloc,kmalloc的参数比较轻松,直接输入自己想要的内存长度即可,由slab分配器去找应该是属于哪个长度分档的,然后由那个分档的kmem_cache结构指针去分配相应长度内存,而kmem_cache_alloc就显得比较“专业”,它不是输入我要多少长度内存,而是直接以kmem_cache结构指针作为参数,直接指定我要这样长度分档的内存,稍微看看这两个函数的调用情况就可以发现它们很快都是调用函数__cache_alloc,只是前面的这些不同而已。
比如现在有一个内核模块想要申请一种它自创的结构,这个结构是111字节,并且它不想获取128字节内存就想获取111字节长度内存,那么它需要在slab分配器中创建一个这样的“规则”,这个规则规定slab分配器当按这种“规则”分配时要给我111字节的内存,这个“规则”的创建方法就是调用函数kmem_cache_create;
同样,内核slab分配器之所以能够默认的提供32-4194304共20种内存长度分档,肯定也是需要创建这样20个“规则”的,这是在初始化时创建的,由函数kmem_cache_init,先不要纠结kmem_cache_init,它里边有一些道理需要在理解slab分配器原理后才能更好的理解,先看kmem_cache_create:
详解slab机制(1) slab是什么相关推荐
- Android 全局大喇叭——详解广播机制
Android 全局大喇叭--详解广播机制 一.广播机制简介 1. 标准广播(Normal broadcasts) 2. 有序广播(Ordered broadcasts) 二.接收系统广播 1. 动态 ...
- Python高级语法-详解set机制
集合过滤机制 文章目录 集合过滤机制 简介 Set详解 Set使用 补充说明 简介 底层分析set的实现原理. 详细分析Python的set容器如何过滤自定义类. 代码实现多熟悉自定义类的按照指定属性 ...
- 第一行代码学习笔记第五章——详解广播机制
知识点目录 5.1 广播机制 5.2 接收系统广播 * 5.2.1 动态注册监听网络变化 * 5.2.2 静态注册实现开机广播 5.3 发送自定义广播 * 5.3.1 发送标准广播 * 5.3.2 发 ...
- 一个普通handler会持有activity引用吗_详解handler机制
Android中有很多机制,打开源码最先遇到的应该就是handler机制了,handler主要为了解决线程间通讯的问题,首先看一下handler该怎么用. ##handler的用法 1.在主线程中用h ...
- TCP/IP详解--拥塞控制机制 RTT时间计算相关的算法
基于TCP的端到端的拥塞控制机制 1988年Van Jacobson指出了TCP在控制网络拥塞方面的不足,并提出了"慢启动"(Slow Start)."拥塞避免" ...
- 【JVM】详解类加载机制
JVM的类加载机制 一.类的生命周期 二.类加载的过程 1.加载 2.连接 3.初始化 三.类加载器的介绍 3.1 启动类加载器(根类加载器/引导类加载器)(Bootstrap ClassLoader ...
- HMS华为账号登入全部流程加详解流程机制
安卓登入华为账号获取授权全过程 1.第一步创建一个安卓项目 2.创建完我们先把登入页面的前端ui写出来(源码放在下方) <?xml version="1.0" encodin ...
- 详解注意力机制和Transformer
本文的学习路线:
- 烤仔TVのCCW | 详解 Conflux 的 Blaming 机制(2)
大家好,欢迎收看 Conflux Chain Webinar. 在全民战"疫"的关键时期,宅家的小伙伴不如和烤仔一起学习区块链知识吧. 上学期,我们为大家讲解到为了把区块链共识层和 ...
最新文章
- MySQL中新建用户,新建数据库,用户授权,删除用户,修改密码的相关操作
- IntelliJ IDEA 2020.2.1 发布,Lombok插件可能被官方支持
- 除了超分辨率,AI 结合 RTC 还有哪些技术实践?
- android studio工程特点,Android Studio的重要特性
- java BigDecimal去掉小数点后的零
- 在给定约束下可以使用a,b和c形成的字符串数
- [水动力学] 水动力学期末复习
- Tomcat关于DH算法问题解决办法
- 实时协同编辑的实现: 编辑锁, OT算法
- 程序员,如何做好工作复盘?
- java公众号互推联盟_写公众号15个月以来,这一路上的学习与收获
- java.lang.IllegalAccessException 没有访问权限
- CentOS命令行清空文件内容
- 傍上区块链大腿,e签宝恐仍难高枕无忧?
- Docx怎么转换成Doc
- 谷歌浏览器怎么同步收藏夹,书签云帮助你
- 2022年医院三基考试医院管理考试模拟试题卷及答案
- 西门子S7-1200二轴V80伺服写字案例程序运动控制 写字机自动化机械控制,高速脉冲
- 区域生长算法(附MATLAB代码实现)
- 淘宝sku API接口实例代码