Kernel那些事儿之内存管理(7) --- Slab(上)
前面讲的buddy system算法,分配内存的最小单位是一个页面(例如 4K)。这对于大的内存申请比较适用。可是实际生活中,Kernel经常需要分配小的内存空间,比如几十个字节,这个时候怎么办呢?
不同的人可能会想到不同的解决办法。
对于财大气粗的富人一族,办法很简单:申请一个页面,只用其中的几十字节,剩下的直接丢掉。
对于锱铢必较的穷人一族,就不敢这么挥霍了:申请一个页面,然后记录好页面内哪些内存区用了,哪些还没用,没用的部分可以用来满足其他的内存分配请求。总之务必做到物尽其用。
很不幸,Kernel是个穷人。为了管理小的内存区域,Kernel按照穷人的办法,引入了slab机制。
Slab机制的思想很简单,把内存页面分成一个一个的object,然后以一个object为单位向外提供内存空间。
这样做还带来了一个额外的好处:slab实际充当了cache的角色。Slab会把释放的object放在自己内部结构中,而不是立即返还给buddy system。这样对于新的内存申请,slab就可以把自己留着的object再分配出去,而不用和buddy system打交道。
讲了这么多,是时候看看slab allocator长什么样子了。
Slab机制中主要有三个组成部分:
1) cache 描述符,用来存放管理信息;
2) slab,由一个或多个连续页面组成,里面放着一个个的object;
3) object,被管理的内存单元。
注意,一个cache中存放的都是相同类型的object。
1. Cache 描述符
每个cache是由结构体kmem_cache来表示的。
381 struct kmem_cache {382 /* 1) per-cpu data, touched during every alloc/free */383 struct array_cache *array[NR_CPUS];384 /* 2) Cache tunables. Protected by cache_chain_mutex */385 unsigned int batchcount;386 unsigned int limit;387 unsigned int shared;388389 unsigned int buffer_size;390 u32 reciprocal_buffer_size;391 /* 3) touched by every alloc & free from the backend */392393 unsigned int flags; /* constant flags */394 unsigned int num; /* # of objs per slab */395396 /* 4) cache_grow/shrink */397 /* order of pgs per slab (2^n) */398 unsigned int gfporder;399400 /* force GFP flags, e.g. GFP_DMA */401 gfp_t gfpflags;402403 size_t colour; /* cache colouring range */404 unsigned int colour_off; /* colour offset */405 struct kmem_cache *slabp_cache;406 unsigned int slab_size;407 unsigned int dflags; /* dynamic flags */408409 /* constructor func */410 void (*ctor)(struct kmem_cache *, void *);411412 /* 5) cache creation/removal */413 const char *name;414 struct list_head next;415416 /* 6) statistics */...450 struct kmem_list3 *nodelists[MAX_NUMNODES];454 };
从源代码的注释中可以看出,这个结构体的内容被分为六个部分。其中第六部分,是与debugging相关的统计信息,略去不讲。
前两个部分是与per-cpu相关的信息。
array: 这是per-cpu的object cache。其原理和我们之前讲的per-cpu page frame cache是一样的,未雨绸缪的机制。
batchcount:每次填充和缩减object cache时,申请和释放的object的个数。
limit:每个per-cpu object cache的大小,也就是其最多可以预存多少个object。
shared: 每个cache中,除了为每个CPU准备的per-cpu object cache,还有一个所有CPU共享的object cache。该cache的大小(即其limit值)为 (cachep->shared*cachep->batchcount)。
buffer_size: 正如前面提到的,一个cache中存放的都是相同类型的object,既然是相同类型,其大小肯定一样。buffer_size就是指定了该cache中每个object的大小。
第三和第四部分是与slab管理相关的信息。
flags: 定义了cache的一些全局属性,例如 CFLGS_OFF_SLAB 决定了该cache中slab描述符的位置。
num: 一个slab中包含的object的个数。
gfporder:指定了一个slab包含的连续页面数,即一个slab包含 2^gfporder 个连续页面。
gfpflags: slab包含的页面也是从buddy system中分配的。gfpflags 指定了当为slab申请页面时所使用的GFP标志。
colour, colour_off 以及kmem_list3中的colour_next:这三个成员用于slab coloring 机制中。
slabp_cache:slab描述符有两个可能的存放位置:internal 或 external。Internal是说slab描述符和slab包含的object一起,存放在slab页面中;External是说slab描述符存放在slab页面之外。这里的slabp_cache就指定了 external的slab描述符存放的位置。
slab_size:slab描述符加上object描述符所占用的空间大小。
ctor:object的构造函数。当新创建一个slab时,意味着新创建了num个object,此时就会对每个object执行该构造函数。
第五部分是cache的全局信息。
name:cache的名字。该名字会出现在 /proc/slabinfo 中。
next:系统中所有的cache都会链接在链表 cache_chain中。
在该结构体的最后,是成员nodelists,其类型为 struct kmem_list3 的指针数组。一个cache中所包含的所有slab,都组织在了这里面。
287 /*288 * The slab lists for all objects.289 */290 struct kmem_list3 {291 struct list_head slabs_partial; /* partial list first, better asm code */292 struct list_head slabs_full;293 struct list_head slabs_free;294 unsigned long free_objects;295 unsigned int free_limit;296 unsigned int colour_next; /* Per-node cache coloring */297 spinlock_t list_lock;298 struct array_cache *shared; /* shared per node */299 struct array_cache **alien; /* on other nodes */300 unsigned long next_reap; /* updated without locking */301 int free_touched; /* updated without locking */302 };
一个slab可能的状态有三个:full, free and partial。这三种类型的slab分别组织在了三个链表中:slabs_full, slabs_free, slabs_partial.
free_objects: 所有slab中空闲object的总数。
free_limit:所有slab中空闲object的总数不得超过free_limit,即不允许 free_objects > free_limit.
shared: 每个CPU有一个自己的object cache。这里又有一个object cache,不过是供所有CPU共享的。
next_reap, free_touched: 这两个是由内存回收机制使用的,暂时不讲。
2. Slab描述符
cache中每个slab是由结构体 slab 来表示的。
221 struct slab {222 struct list_head list;223 unsigned long colouroff;224 void *s_mem; /* including colour offset */225 unsigned int inuse; /* num of objs active in slab */226 kmem_bufctl_t free;227 unsigned short nodeid;228 };
list:根据slab的类型,通过list把slab挂在kmem_list3中的链表slabs_full, slabs_free 或 slabs_partial上。
colouroff:该slab中第一个object的页内偏移。
s_mem:该slab中第一个object的地址。
inuse:该slab中有多少object已经被分配出去了。
free:该slab中下一个空闲的object的索引。如果没有空闲的object,该值为BUFCTL_END。
3. Object描述符
每个object也有一个描述符,类型为kmem_bufctl_t。这个描述符就简单多了,因为它实际上就是无符号整型。
208 typedef unsigned int kmem_bufctl_t;
所有的object描述符紧挨着slab描述符存放,两者总是基情满满的粘在一块。所以,和slab描述符一样,object描述符也有两个可能的存放位置:
External object 描述符:存放在slab页面之外,具体位置由cache描述符中的slabp_cache指出。
Internal object 描述符:存放在slab页面之内。
第 k 个object描述符描述第 k 个object,且只有该object为空闲时才有意义,此时它包含了该slab中下一个空闲object的索引。这样,同一个slab中所有的空闲object 就形成了一个简单的链表。该链表中最后一个object描述符值为BUFCTL_END,表示链尾。
4. Object cache
前面讲buddy system时,讲过一个“未雨绸缪”的机制。当时说过,这是Kernel的一个惯用伎俩,这不,在这里我们又看到了这个伎俩。
该伎俩思想很简单:提前分配好一些资源放在一个per-cpu cache中。这样做可以减少不同CPU之间对锁的竞争,也可以减少对slab中各种链表的操作。
object cache由结构体 array_cache 来表示。
264 struct array_cache {265 unsigned int avail;266 unsigned int limit;267 unsigned int batchcount;268 unsigned int touched;269 spinlock_t lock;270 void *entry[]; 275 };
avail:该object cache中可用object的个数。同时也作为该cache中第一个空槽的索引,这一点有点像stack中的栈顶指针。所以object cache是一个LIFO的数据结构。
limit:该object cache的大小。
batchcount:填充或缩减cache时的chunck size。
touched:如果该object cache最近被使用过,则touched置为1.
entry:这是一个dummy array。object cache中所有的object紧跟着该描述符存放。
正如slab描述符和object描述符一样,object cache描述符和其包含的objects也是基情满满的黏在一起的,这些objects 紧跟在描述符后面,其地址由描述符中的entry指出。
注意,这里所说的object,其实是object的地址。
前面讲过,每个CPU都有一个object cache,放在cache描述符的成员变量 array中。除此之外,还有一个所有CPU共享的object cache,放在kmem_list3结构体的成员变量shared中。
这个共享cache的存在,使得从一个CPU的object cache中往另外一个CPU的object cache中移动object的任务变得简单了许多。
至此,我们讲完了slab机制所使用的所有结构体,这些结构体的相互关系大约是这个样子。
5. Slab coloring
如果不考虑slab coloring,一个cache中,由于所有的object大小相同,所以不同slab中相同索引的object,其offset相同。这本不是什么问题,但是如果考虑进hardware cache,具有相同offset的objects很可能会被放在同一个 cache line中,这个很影响性能的。
Slab coloring正是为了解决这个问题引入的。其思想是:在各个slab的开始位置,插入大小不等的一点空间,这样各个slab中相同索引的object就会有不同的offset了。而插入的这个空间,就称为该slab的color。
那要插入的这些空间又是从哪来的呢?一个slab,被分成一个个object后,很有可能会留下一点空间,这些空间不足够再分出一个object,所以只能浪费掉了。就像你拿一块布去做衣服,再好的裁缝,总也会留下点布头、下脚料啥的。
可是Kernel就连这点下脚料都不放过,把它们分成不同的color供slab使用。可见‘物尽其用’这四个字,Kernel真是做到极致了。
让我们来看一下cache中object是怎么存放的。假设一个cache中所有的object都是对齐的,也就是说所有object的内存地址是某个数 (假设为 aln) 的整数倍。
我们定义这样几个变量:
num: 一个slab中object的个数。
osize: 每个object的大小。
dsize: slab描述符和object描述符一共所占空间的大小。如果是external的slab,则该值为0。
free: 该slab中没有用到的空间的大小,即下脚料的大小。free 肯定是小于 osize的,否则该slab就能再安排出一个object了。不过 free 是可以比 aln 大的。
那么一个slab的大小可以表示为:
slab length = (num * osize) + dsize + free
所谓slab coloring,其实就是把一部分free的空间,从slab的尾部移到slab头部,同时还要满足对齐要求。所以可用的color数为 (free/aln) 。cache描述符中的成员变量 colour,保存的正是这个值。
这些个color会在不同的slab之间平均地分布。下一个要使用的color值保存在结构体kmem_list3的成员变量colour_next中。当创建一个新的slab时,新slab会用colour_next作为自己的color,然后colour_next递增加1。如果增加到了最大值 cachep->colour,那么colour_next变成0,重新开始。这样可以保证,每个slab和其前一个slab,使用的是不同的color。
另外,aln的值会保存在cache描述符中的成员变量colour_off中;而slab描述符中的colouroff,保存的是 (color * aln) + dsize的值,即第一个object的偏移。
转载于:https://blog.51cto.com/richardguo/1673269
相关文章:

Vyond制作2D动画学习教程
Vyond为2D动画提供了极其简单的分解视频创建过程。 你会学到什么 课程获取:Vyond制作2D动画学习教程-云桥网 您将学习如何为2d动画制作画外音 您将学习如何使用Vyond轻松创建精彩的动画视频 流派:电子学习| MP4 |视频:h264,1280720 |音频:AAC…

Blender3.0电影级别CG场景制作视频教程
Blender 3.0的电影场景制作–8小时以上的主教程 三本教程合三为一 你会学到什么 你将学习如何在Blender中建模 你将学习如何在Blender中创建更复杂的纹理 你将学习如何创建一个场景,设置摄像机和灯光 你将在Blender中学习动画的基础知识 你将会学到如何在Blender中…

Android WebView使用与JavaScript使用
WebView基本使用 WebView是View的一个子类,可以让你在activity中显示网页。 可以在布局文件中写入WebView:比如下面这个写了一个填满整个屏幕的WebView: <?xml version"1.0" encoding"utf-8"?> <WebView x…

YOLOv8-Detect训练CoCo数据集+自己的数据集
至此,整个训练预测阶段完成。此过程同样可以在linux系统上进行,在数据准备过程中需要仔细,保证最后得到的数据准确,最好是用显卡进行训练。有问题评论区见!

lvs服务器需要开启web服务么_Nginx+Keepalived实现web服务器高可用
1、Nginx业务背景现公司需求快速搭建web服务器,对外提供给用户web服务。需求拆分需要基于http协议的软件,搭建服务实现介绍常见用法:1) web服务器软件 httpd http协议同类的web服务器软件:apache(老牌) nginx(俄罗斯) IIS(微软)2)…

centos使用镜像源轻松配置golang+vscode的方法
Title:centos使用镜像源轻松配置golangvscode的方法 (阅读时间:约5分钟) 零.序言 最近笔者在上一门名为服务计算的课程,在老师的作业博客中提到,安装golangvscode环境总会遇到各种网络问题,在…

MySQL_update同一张表
update tb1 inner join(select type, count(*) as cntfrom tb1 group by type)as der using(type)set tb1.cnt der.cnt转载于:https://www.cnblogs.com/MarchThree/p/4641515.html

在SolidWorks 3D CAD中构建乐高直升机
以有趣而独特的方式练习您的3D CAD技能 你会学到什么 练习SolidWorks三维CAD的使用 学习如何使用机械伴侣 创建直升机的零件 将零件组装成工作模型 类型:电子教学| MP4 |视频:h264,1280720 |音频:AAC,48.0 KHz 语言:英语中英文字幕…

CSS局限属性contain:优化渲染性能的利器
在网页开发中,优化渲染性能是一个重要的目标。CSS局限属性contain是一个强大的工具,可以帮助我们提高网页的渲染性能。本文将介绍contain属性的基本概念、用法和优势,以及如何使用它来优化网页的渲染过程。

利用java多线程向MongoDB中批量插入静态文件
第一步、开发环境: win7 64位(注:MongoDb在32位windows上有数量限制(2G),详见官方文档) Mongodb3.2 mongofb_java_driver 3.2.2 第二部、安装mongodb,并开启服务 略:可参见官方文档 第三部、代码 import java.io.BufferedReader; …

为啥我从后台查到的值在页面显示的是undefined_再谈一个管理后台列表功能应有的素质...
大家能看到的这个号第1篇文章《无心朝政,列表功能分析下》就是讲列表功能。虽然当时写的时候特别认真,但基本是围绕“列表功能”这个广泛的词来阐述的。最近在做一个体育赛事赛程的后台管理系统 Match-Schedule,对管理后台列表有更多的体会…

Go语言的错误异常处理机制及其应用
一、背景 在日常编写golang程序或阅读别人的golang代码时,我们总会看到如下的一堆代码块: xx, err func(xx) if err ! nil {//do sth. to tackle this problem }这种经典的显式错误处理方式,在golang开发中几乎无处不在,了解过…

【Win10 应用开发】语音命令与App Service集成
昨天,老周演示了语音命令集成这一高大上功能,今天咱们来点更高级的语音命令。 在昨天的例子中,响应语音命令是需要启动应用程序的,那么如果可以不启动应用程序,就直接在小娜面板上进行交互,是不是会更高大小…

Maya 2022中的硬表面建模技术学习视频教程
Maya 2022中的硬表面建模 信息: 使用正确的拓扑和边流在Maya中建模硬曲面对象。 你会学到什么 硬质表面工具和技术 细分工作流程 边缘流动控制 正确拓扑 材料设置 Studio Lightning 渲染 持续时间16小时30分钟 1280X720 MP4 大小解压后:11.5G 语言:英…

六年级小学python第四讲_python第四讲
列表的排序 升序:从小到大的排序 降序:从大到小的排序 示例: num_list [120,250,11,44,77,45,22,390] num_list.sort() # 升序 - 从小到大 print(num_list) # 对列表本身做了修改 num_list.sort(reverseTrue)#降序 为False#升序 print(num_list) 反转 p…

android之AlertDialog 点击其它区域自己主动消失
遇到一个问题记录下来,在开发中使用了AlertDialog,想点击屏幕其它区域的时候让这个dialog消失,一開始不做不论什么设置,在小米手机能够正常显示,可是在三星中却有问题。后来发现少了一个属性: View dilaogV…

中级实训第一天的自学报告
目录【阅读时间:约10分钟】中级实训第一天的自学报告姓名:隐藏敏感信息 学号:隐藏敏感信息一、Vi/Vim二、Java三、Ant四、Junit【附加】五、SonarQube【附加】六、 编译运行BugRunner七、 总结中级实训第一天的自学报告 姓名:隐藏…

完整的Blender三维课程:素描到三维艺术的初学者
使用Blender创建3D艺术。初级课程 你会学到什么 使用Blender并理解它的界面 创建3D模型 创建您自己的材料 布料仿真、纹理和雕刻 第三种解释的相机法则 照相机景深 灯光设置 渲染 如何使用参考素材进行3D素描和建模?参考镜头也包括在内。 课程中包含所有章节场景文…

VirtualBox: Effective UID is not root
为什么80%的码农都做不了架构师?>>> 桌面上运行virtualbox出错: The virtual machine xp has terminated unexpectedly during startup with exit code 1 (0x1). Effective UID is not root(euid1000 egid482 uid1000 gid482)(rc-10) Please try reinst…

ironpython 教程_「ironpython」VS2017 IronPython做界面
本人开始做毕设了,但老师说工具要有界面,所以就开始找Python做界面的东西……之前做过C#的界面,脱拉拽很快界面就完成了,后来我查了下IronPython是用C#写的python解释器,也可以脱拉拽做界面,于是就开始尝试…

4、jQuery实现的全选、反选和不选功能
2019独角兽企业重金招聘Python工程师标准>>> 这个地址更权威:最好的选择:http://www.sucaihuo.com/js/10.html <html><head><title>TODO supply a title</title><meta charset"UTF-8"><meta name…

构建自己的PHP框架--构建缓存组件(1)
作为一个框架,我们还没有相应的缓存组件,下面我们就来构建我们的缓存组件。 先来定义一下接口,在 src 文件夹下创建 cache 文件夹,在cache文件夹下创建 CacheInterface.php 文件,其中定义 Cache 相应的接口,…

基于Golang的CLI 命令行程序开发
基于Golang的CLI 命令行程序开发 【阅读时间:约15分钟】一. CLI 命令行程序概述二. 系统环境&项目介绍&开发准备1.系统环境2.项目介绍3.开发准备三.具体程序设计及Golang代码实现1.selpg的程序结构2.导入的库3.sp_args结构体4.全局变量4.main函数5.process_…

Photoshop创造氛围照片合成视频教程含素材
数百个高分辨率工具和9个完整项目! 这个包包含开始创建史诗大气合成所需的一切-超过四个小时的基于项目的深入教程和超过400个合成工具。 大小解压后:10.7G 含课程素材文件 1920X1080 mp4 语言:英语中英文字幕(根据原英文字幕机…

java c++的区别_Java语言与C、C++之间的区别?
小伙伴你知道吗?Java是由C开发而来的,并且在当时一直被搁置。随着Java语言的崛起那么Java和C/C有什么不同和相同之处呢?通过上述我们知道那Java前身是C,并且保留了C的大部分内容,其编程方式也类似于C。但Java的句法更清…

Android编程获取网络连接状态及调用网络配置界面
获取网络连接状态 随着3G和Wifi的推广,越来越多的Android应用程序需要调用网络资源,检测网络连接状态也就成为网络应用程序所必备的功能。 Android平台提供了ConnectivityManager 类,用于网络连接状态的检测。 Android开发文档这样描述Connec…

学习在Unity中创建一个动作RPG游戏
游戏开发变得简单。使用Unity学习C#并创建您自己的动作角色扮演游戏! 你会学到什么 学习C#,一种现代通用的编程语言。 了解Unity中2D发展的能力。 发展强大的和可移植的解决问题的技能。 了解游戏开发流程。 了解面向对象编程在实践中是如何工作的。 Le…

python数组对应元素相乘_python的几种矩阵相乘的公式详解
1. 同线性代数中矩阵乘法的定义: np.dot() np.dot(A, B):对于二维矩阵,计算真正意义上的矩阵乘积,同线性代数中矩阵乘法的定义。对于一维矩阵,计算两者的内积。见如下Python代码: import numpy as np # 2-D…

c#属性中的get和set属性
get是给属性赋值,set是取属性的值。 get、set用法: 一是隐藏组件或类内部的真是成员; 二是用来建立约束的,比如,实现“有我没你”这种约束; 三是用来响应属性变化事件,当属性变化是做某事&#…

基于Golang的监听读取配置文件的程序包开发——simpleConfig_v1
基于Golang的监听&读取配置文件的程序包开发——simpleConfig_v1 【阅读时间:约10分钟】 一、配置文件概述二、系统环境&项目介绍1.系统环境2.项目的任务要求三、具体程序设计及Golang代码实现1. 数据结构2. init函数模块3.listen函数模块4.watch函数模块四…