anki
- Markdown and KaTeX Support
md 支持(可以写代码好好好)
SLUB
图解 SLUB 好直观的示例图
The SLUB allocator 清晰的笔记
是什么
- 用途
slab 以字节为单位给结构体之类的东西分配空间。相对于 buddy 分配器分配大的空间,slab 分配器仍然从 Buddy 分配器中申请内存,之后自己对申请来的内存细分管理。
- 命令查看
可以通过命令 sudo cat /proc/slabinfo
查看 slab 的使用情况。不过 wsl 没有 slab,不懂为啥wsl 内核不使用 slab。
实现源码
- include/linux/slab_def.h
- include/linux/slab.h
- mm/slab.c
主要 api
void *kmalloc(size_t size, gfp_t flags)
kfree
其中的 flag 可以选择的参数是一些列 GFP_xxx 的标志,比如
__GFP_DMA
关键结构体
听说内核的内存比用户态更简单,因为matadata不是和data本身存在一起,而是保存在另外的结构体。真的假的。
metadata 大概指的是这个内存的信息
按照的顺序,如果直接从顶层结构体开始看会接触很多不了解的概念。
struct slab(struct page )
slab 结构体 用于记录一个或多个连续页面,被划分成的特定的结构体数组。关键的结构体成员有
void *freelist
首个空闲的结构体struct list_head slab_list
这个 slab 属于哪一个链表struct kmem_cache *slab_cache
这个这个 slab 属于的 cache
有趣的是,为了节省空间(毕竟每一个被分配的页都需要有一个 slab 结构体,产生大量的开销),将 slab 结构体折叠到了原有的 page 结构体当中。这篇文章介绍了 page 结构体是怎么被塞入更多内容。
这些 slab 可以分为 3 类:
- full :已经完全分配的 slab
- partial: 部分分配的slab
- free:空slab,或者没有对象被分配
kmem_cache_cpu
kmem_cache_cpu 是每个 cpu 用于管理正在使用中的 slab 以及可以供使用的 slab 的结构体。这是一个 per CPU 变量,因此每个 CPU 都是独有一份的。其中的关键结构体说明如下(结合上面的图看更直观一些)
void **freelist
下一刻可供分配的 objectpage
下一个可用的 page (就是 freelist 的所在的 page)partial
见下文:
partial
当图中右下角full slab释放obj的时候,首先就会将slab挂入per cpu partial链表管理。通过struct page中next成员形成单链表。per cpu partial链表指向的第一个page中会存放一些特殊的数据。例如:pobjects存储着per cpu partial链表中所有slab可供分配obj的总数,如图所示。当然还有一个图中没有体现的pages成员存储per cpu partial链表中所有slab缓存池的个数。pobjects到底有什么用呢?我们从full slab中释放一个obj就添加到per cpu partial链表,总不能无限制的添加吧!因此,每次添加的时候都会判断当前的pobjects是否大于kmem_cache的cpu_partial成员,如果大于,那么就会将此时per cpu partial链表中所有的slab移送到kmem_cache_node的partial链表,然后再将刚刚释放obj的slab插入到per cpu partial链表。如果不大于,则更新pobjects和pages成员,并将slab插入到per cpu partial链表
kmem_cache_node
per node partia链表类似per cpu partial,区别是node中的slab是所有cpu共享的,而per cpu是每个cpu独占的。假如现在的slab布局如上图所示。假如现在如红色箭头指向的obj将会释放,那么就是一个empty slab,此时判断kmem_cache_node的nr_partial是否大于kmem_cache的min_partial,如果大于则会释放该slab的内存。
kmem_cache
每一种大小或者类型的缓存都由 kmem_cahce
管理
重要字段比如
const char *name
: 缓存的名字。比如说kmalloc-32
unsigned int object_size
: 大小(不算metadata)unsigned int object_size
: 算上metadata 的大小struct kmem_cache_cpu __percpu *cpu_slab
:kmem_cache_cpu
结构体指针struct kmem_cache_node *node[MAX_NUMNODES]
: 指针列表kmem_cache_node
unsigned int offset
: 见下问 kfreeunsigned long random
: 见下文 kfreeunsigned int *random_seq
: 见下文 kfree
kfree
当一个对象被 free 时,它们将被添加到板 freelist 中。 kmem_cache_cpu.freelist
指向了 freelist 中的第一个对象。freelist 中每个后续对象的地址都存储在前一个 free 对象内的偏移为 kmem_cache.offset
的位置。
关于 freelist 的保护有
Hardened Freelist
CONFIG_SLAB_FREELIST_HARDENED
安全加固宏是给freelist
链表指针进行混淆:
混淆后的指针=原指针 ^ 随机数random ^ 指针地址
Freelist randomization
CONFIG_SLAB_FREELIST_RANDOM
安全加固是将freelist 列表顺序随机化,正常freelist列表就是从开始往后按顺序排列,但如果开启了CONFIG_SLAB_FREELIST_RANDOM 则会打乱freelist 中object 的顺序,即便是刚申请好的新slab,其中的freelist 列表顺序也是随机的.
kmalloc
内核代码
内核代码一般在这个目录 /usr/src/linux
。目录组成如下:
arch
: 内核支持的硬件体系的代码
include
: inlcude 文件
init
: 启动代码
mm
内存管理
drivers
驱动
ipc
进程通讯
fs
文件系统
kernel
主要核心代码
net
网络
lib
库代码
scripts
配置脚本
per-CPU
根据CPU的个数,在内存中生成多份拷贝,并且能够根据变量名和CPU编号,正确的对各个CPU的变量进行寻址。
- 可以 CPU 之间不会相互访问,减少上锁的需要
- 如果一个处理器操作另一个处理器缓存中保存的数据,则该处理器必须刷新或以其他方式更新其缓存。per CPU变量可以减少 缓存刷新
Reasons for Using Per-CPU Data
cpio
解压
cpio -idmv -D ./fs < rootfs.img
-i
解压
-d
按照压缩文件的目录解压
-m
按照原本的时间戳
-v
显示详细信息
-D
切换目录