【转载】内存基本概念-slab算法

发布时间 2023-12-22 22:27:59作者: 嵌入式与Linux那些事

Linux内存管理之slab 2:slab API
https://blog.csdn.net/lqy971966/article/details/119801912

1. 为什么有了Buddy(伙伴系统)还需要slab?

1.1 什么是伙伴系统?

Linux内核中使用伙伴系统(buddy system)算法以页为单位管理内存,进行内存分配。

1.1.1 伙伴系统思想

它把所有的空闲页放到11个链表中,每个链表分别管理大小为1,2,4,8,16,32,64,128,256,512,1024个页的内存块。当系统需要分配内存时,就可以从buddy系统中获取。

1.2 伙伴系统例子说明

例如,要申请一块包含4个页的连续内存,就直接从buddy系统中管理4个页连续内存的链表中获取。同样的,如果系统需要申请3个页的连续内存,则只能在4个页的链表中获取,剩下的一个页被放到buddy系统中管理1个页的链表中。

1.3 伙伴系统能解决的问题

Buddy 系统解决了物理内存分配的外部碎片问题。

Linux内存管理 - buddy系统
https://www.cnblogs.com/longchang/p/10749392.html

2 为什么需要引入slab算法?

2.1 伙伴系统的缺点

Buddy提供了以page为单位的内存分配接口,这对内核来说颗粒度还太大了,所以需要一种新的机制,将page拆分为更小的单位来管理。

2.2 伙伴系统的缺点例子说明

Linux内存以页为单位进行内存管理,buddy算法以2的n次方个页面来进行内存分配管理,最小为20,也就是一页,最大为211,就是4MB大小的连续内存空间。但是页的粒度还是太大,Linux下是4KB大小,也就是4096个字节,而kernel本身有很多数据结构时时刻刻都需要分配或者释放,这些数据的大小又往往小于4KB大小,一般只有几个几十个字节这样的大小。
比方最常用到的task_struct(进程描述符)结构体和mm_struct(内存描述符)结构体,其中,izeof task_struct = 9152,sizeof mm_struct = 2064。

task_struct稍微大一点将近2个页面,mm_struct就只有差不多半个页面了。这样一来如果所有的这些数据结构都按照页来分配存储和管理,那么我相信kernel过不了多久自己就玩完了,内存碎片肯定一堆一堆。

所以,引入slab分配器是为了弥补内存管理粒度太大的不足。

3. slab 能解决什么问题?

slab分配需要解决的是内存的内部碎片问题。
所谓内部碎片就是指被内核分配出去但是不能被利用的内存。
而外部碎片是指由于频繁地申请和释放页框而导致的某些小的连续页框,比方只有一个页框,无法分配给需要大的连续页框的进程而导致的内存碎片。

3.3 slab 分配例子

比如我需要一个100字节的连续物理内存,那么内核slab分配器会给我提供一个相应大小的连续物理内存单元,为128字节大小(不会是整好100字节,而是这个档的一个对齐值,如100字节对应128字节,30字节对应32字节,60字节对应64字节)

4. slab系统核心思想是什么?

4.0. 须知:

kmem_cache: 内存池
slab:       内存池从系统申请内存的基本单位
object:     内存池提供的内存的单位
123

4.1 slab核心思想:对象管理内存(简单说:就是你经常用什么我先给你创造一堆出来,你要用直接拿,不用放回来)

使用对象的概念来管理内存。

slab分配器的基本思想是,先利用页面分配器分配出单个或者一组连续的物理页面,然后在此基础上将整块页面分割成多个相等的小内存单元,以满足小内存空间分配的需要。当然,为了有效的管理这些小的内存单元并保证极高的内存使用速度和效率。

这段话摘自:《深入linux设备驱动程序内核机制》
https://blog.csdn.net/yuzhihui_no1/article/details/47305361

4.2 slab的对象/内存池思想

在内核中,经常会使用一些链表,链表中会申请许多相同结构的结构体,比如文件对象,进程对象等等,如果申请比较频繁,那么为它们建立一个内存池,内存池中都是相同结构的结构体,当想申请这种结构体时,直接从这种内存池中取一个结构体出来,是有用且速度极快的。一个物理页就可以作用这种内存池的载体,进而进行充分利用,减少了内部碎片的产生。

所以,Slab 相当于内存池思想,且是为了解决内碎片而产生的,slab的核心思想是以对象的观点管理内存。

4.3 slab中对象是什么?

所谓的对象就是存放一组数据结构的内存区,为便于理解可把对象看作内核中的数据结构(例如:task_struct,file_struct 等)。

相同类型的对象归为一类,每当要申请这样一个对象时,slab分配器就从一个slab列表中分配一个这样大小的单元出去,而当要释放时,将其重新保存在该列表中,而不是直接返回给伙伴系统,从而避免内部碎片。

4.4 cache是内存中的区域,而不是指硬件高速缓存

这种场景是非常多的,为了应对这种场景,slab为这样的对象创建一个cache,即缓存。每个cache所占的内存区又被划分多个slab,每个 slab是由一个或多个连续的页框组成。每个页框中包含若干个对象,既有已经分配的对象,也包含空闲的对象。

尽管英文中使用了Cache这个词,但实际上指的是内存中的区域,而不是指硬件高速缓存

4.5 lab分配器的一个基本原则

slab分配器对不同长度内存是分档的,这是slab分配器的一个基本原则,按申请的内存的大小分配相应长度的内存。

这可以先参考kmalloc的实现,kmalloc申请的物理内存长度为参数size,它需要先根据这个长度找到相应的长度的缓存
slab分配器并非一开始就能智能的根据内存分档值分配相应长度的内存。每种cache对应一种长度的slab分配。

5 slab分配的最小和最大内存单元是多少?

23、………211个字节。
另外还有两个特殊的组,分别是96B和192B,共11组

所以,slab分配内存大小是:
8B,16B,32B,64B,96B,128B,192B,256B,512B,1024B,2048B等大小。共11组

5.1 cat /proc/slabinfo 查看系统中的slab对象

[root@localhost 10]# cat /proc/slabinfo
slabinfo - version: 2.1
# name  <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
rpc_inode_cache       51     51    640   51    8 : tunables    0    0    0 : slabdata      1      1      0
xfs_dqtrx              0      0    528   62    8 : tunables    0    0    0 : slabdata      0      0      0
xfs_dquot              0      0    488   67    8 : tunables    0    0    0 : slabdata      0      0      0
xfs_ili             2160   2160    168   48    2 : tunables    0    0    0 : slabdata     45     45      0
xfs_inode          21352  21352    960   34    8 : tunables    0    0    0 : slabdata    628    628      0
xfs_efd_item         156    156    416   39    4 : tunables    0    0    0 : slabdata      4      4      0
xfs_btree_cur         39     39    208   39    2 : tunables    0    0    0 : slabdata      1      1      0
xfs_log_ticket        44     44    184   44    2 : tunables    0    0    0 : slabdata      1      1      0
bio-2                 51     51    320   51    4 : tunables    0    0    0 : slabdata      1      1      0
kcopyd_job             0      0   3312    9    8 : tunables    0    0    0 : slabdata      0      0      0
dm_uevent              0      0   2608   12    8 : tunables    0    0    0 : slabdata      0      0      0
dm_rq_target_io        0      0    136   60    2 : tunables    0    0    0 : slabdata      0      0      0
ip6_dst_cache         72     72    448   36    4 : tunables    0    0    0 : slabdata      2      2      0
RAWv6                286    286   1216   26    8 : tunables    0    0    0 : slabdata     11     11      0
UDPLITEv6              0      0   1216   26    8 : tunables    0    0    0 : slabdata      0      0      0
UDPv6                 26     26   1216   26    8 : tunables    0    0    0 : slabdata      1      1      0
tw_sock_TCPv6          0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
TCPv6                 15     15   2176   15    8 : tunables    0    0    0 : slabdata      1      1      0
cfq_queue             70     70    232   70    4 : tunables    0    0    0 : slabdata      1      1      0
bsg_cmd                0      0    312   52    4 : tunables    0    0    0 : slabdata      0      0      0
mqueue_inode_cache    36     36    896   36    8 : tunables    0    0    0 : slabdata      1      1      0
hugetlbfs_inode_cache 53     53    608   53    8 : tunables    0    0    0 : slabdata      1      1      0
configfs_dir_cache     0      0     88   46    1 : tunables    0    0    0 : slabdata      0      0      0
dquot                  0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
userfaultfd_ctx_cache  0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
fanotify_event_info   73     73     56   73    1 : tunables    0    0    0 : slabdata      1      1      0
pid_namespace          0      0   2200   14    8 : tunables    0    0    0 : slabdata      0      0      0
posix_timers_cache    66     66    248   66    4 : tunables    0    0    0 : slabdata      1      1      0
UDP-Lite               0      0   1088   30    8 : tunables    0    0    0 : slabdata      0      0      0
flow_cache             0      0    144   56    2 : tunables    0    0    0 : slabdata      0      0      0
xfrm_dst_cache         0      0    576   56    8 : tunables    0    0    0 : slabdata      0      0      0
UDP                   30     30   1088   30    8 : tunables    0    0    0 : slabdata      1      1      0
tw_sock_TCP            0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
TCP                   16     16   1984   16    8 : tunables    0    0    0 : slabdata      1      1      0
dax_cache             42     42    768   42    8 : tunables    0    0    0 : slabdata      1      1      0
blkdev_queue          26     26   2424   13    8 : tunables    0    0    0 : slabdata      2      2      0
blkdev_ioc            78     78    104   39    1 : tunables    0    0    0 : slabdata      2      2      0
user_namespace        68     68    480   68    8 : tunables    0    0    0 : slabdata      1      1      0
dmaengine-unmap-128     30     30   1088   30    8 : tunables    0    0    0 : slabdata      1      1      0
sock_inode_cache    1020   1020    640   51    8 : tunables    0    0    0 : slabdata     20     20      0
fsnotify_mark_connector 129370 129370     24  170    1 : tunables    0    0    0 : slabdata    761    761      0
net_namespace          6      6   5248    6    8 : tunables    0    0    0 : slabdata      1      1      0
shmem_inode_cache   1200   1200    680   48    8 : tunables    0    0    0 : slabdata     25     25      0
Acpi-ParseExt       6440   6440     72   56    1 : tunables    0    0    0 : slabdata    115    115      0
Acpi-State           102    102     80   51    1 : tunables    0    0    0 : slabdata      2      2      0
task_delay_info      288    288    112   36    1 : tunables    0    0    0 : slabdata      8      8      0
taskstats             49     49    328   49    4 : tunables    0    0    0 : slabdata      1      1      0
proc_inode_cache    1621   1813    656   49    8 : tunables    0    0    0 : slabdata     37     37      0
sigqueue              51     51    160   51    2 : tunables    0    0    0 : slabdata      1      1      0
bdev_cache            39     39    832   39    8 : tunables    0    0    0 : slabdata      1      1      0
kernfs_node_cache  35360  35360    120   68    2 : tunables    0    0    0 : slabdata    520    520      0
mnt_cache           2478   2478    384   42    4 : tunables    0    0    0 : slabdata     59     59      0
inode_cache        16720  16720    592   55    8 : tunables    0    0    0 : slabdata    304    304      0
dentry             49350  49350    192   42    2 : tunables    0    0    0 : slabdata   1175   1175      0
iint_cache             0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
selinux_inode_security  45798  45798     40  102    1 : tunables    0    0    0 : slabdata    449    449      0
buffer_head         2145   2145    104   39    1 : tunables    0    0    0 : slabdata     55     55      0
vm_area_struct      4699   4699    216   37    2 : tunables    0    0    0 : slabdata    127    127      0
mm_struct             60     60   1600   20    8 : tunables    0    0    0 : slabdata      3      3      0
files_cache          204    204    640   51    8 : tunables    0    0    0 : slabdata      4      4      0
signal_cache         252    252   1152   28    8 : tunables    0    0    0 : slabdata      9      9      0
sighand_cache        255    255   2112   15    8 : tunables    0    0    0 : slabdata     17     17      0
task_xstate          273    273    832   39    8 : tunables    0    0    0 : slabdata      7      7      0
task_struct          255    264   4048    8    8 : tunables    0    0    0 : slabdata     33     33      0
anon_vma            1887   1887     80   51    1 : tunables    0    0    0 : slabdata     37     37      0
shared_policy_node   2635   2635     48   85    1 : tunables    0    0    0 : slabdata     31     31      0
numa_policy          186    186    264   62    4 : tunables    0    0    0 : slabdata      3      3      0
radix_tree_node     2856   2856    584   56    8 : tunables    0    0    0 : slabdata     51     51      0
idr_layer_cache      180    180   2112   15    8 : tunables    0    0    0 : slabdata     12     12      0
dma-kmalloc-8192       0      0   8192    4    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-4096       0      0   4096    8    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-2048       0      0   2048   16    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-1024       0      0   1024   32    8 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-512       64     64    512   64    8 : tunables    0    0    0 : slabdata      1      1      0
dma-kmalloc-256        0      0    256   64    4 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-128        0      0    128   64    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-64         0      0     64   64    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-32         0      0     32  128    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-16         0      0     16  256    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-8          0      0      8  512    1 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-192        0      0    192   42    2 : tunables    0    0    0 : slabdata      0      0      0
dma-kmalloc-96         0      0     96   42    1 : tunables    0    0    0 : slabdata      0      0      0
kmalloc-8192          40     40   8192    4    8 : tunables    0    0    0 : slabdata     10     10      0
kmalloc-4096         254    264   4096    8    8 : tunables    0    0    0 : slabdata     33     33      0
kmalloc-2048         955   1072   2048   16    8 : tunables    0    0    0 : slabdata     67     67      0
kmalloc-1024        2894   2976   1024   32    8 : tunables    0    0    0 : slabdata     93     93      0
kmalloc-512         4825   4864    512   64    8 : tunables    0    0    0 : slabdata     76     76      0
kmalloc-256         7406   7552    256   64    4 : tunables    0    0    0 : slabdata    118    118      0
kmalloc-192         7560   7560    192   42    2 : tunables    0    0    0 : slabdata    180    180      0
kmalloc-128         3136   3136    128   64    2 : tunables    0    0    0 : slabdata     49     49      0
kmalloc-96          5334   5334     96   42    1 : tunables    0    0    0 : slabdata    127    127      0
kmalloc-64         84850  85184     64   64    1 : tunables    0    0    0 : slabdata   1331   1331      0
kmalloc-32        125696 125696     32  128    1 : tunables    0    0    0 : slabdata    982    982      0
kmalloc-16         63232  63232     16  256    1 : tunables    0    0    0 : slabdata    247    247      0
kmalloc-8          86528  86528      8  512    1 : tunables    0    0    0 : slabdata    169    169      0
kmem_cache_node      128    128     64   64    1 : tunables    0    0    0 : slabdata      2      2      0
kmem_cache           128    128    256   64    4 : tunables    0    0    0 : slabdata      2      2      0
[root@localhost 10]#
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101

6. slab与buddy系统的关系

6.1 互补的

slab系统与buddy系统所要解决的问题是互补的,一个解决外部碎片一个解决内部碎片,但事实上,slab在新建cache时同样需要用到buddy来为之分配页面,而在释放cache时也需要buddy来回收这此页面。也就是说,slab事实上是依赖buddy系统的。

6.2 实际还是伙伴系统分配物理页

从slab的分配可以知道,其实所有的内存最终还是要伙伴系统来分配,这里就可以知道,这些内存都是连续的物理页。

6.3 slab作为内核缓存对象

在某些情况下内核模块可能需要频繁的分配和释放相同的内存对象,这时候slab可以作为内核对象的缓存,当slab对象被释放时,slab分配器并不会把对象占用的物理空间还给伙伴系统。这样的好处是当内核模块需要再次分配内存对象时,不需要那么麻烦的向伙伴系统申请,而是可以直接在slab链表中分配一个合适的对象

7. slab 配器结构

7.1 slab 结构图

图:
在这里插入图片描述

解释:
每个kmem_cache都是链接在一起形成一个全局的双向链表,由cache指向该链表,系统可以从Cache_chain开始扫描每个kmem_cache,来找到一个大小最合适的kmem_cache,然后从该kmem_cache中分配一个对象

7.2 slab 结构解释

7.2.1 cache_chain

最高层是 cache_chain,这是一个 slab 缓存的链接列表。可以用来查找最适合所需要的分配大小的缓存(遍历列表)
cache_chain 的每个元素都是一个 kmem_cache 结构的引用(称为一个 cache)。它定义了一个要管理的给定大小的对象池。

7.2.2 kmem_cache

kmem_cache: 内存池
slab:       内存池从系统申请内存的基本单位
object:     内存池提供的内存的单位
123
7.2.2.1 kmem_cache 或 cache:
有的地方也会叫这个kmem_cache为cache,原因是kmem_cache中的object有大有小(其实也是kmem_cache有大有小),
当内存申请时,会有命中该kmem_cache的说法,和CPU中的cache命中是类似的意思,所以也会叫kmem_cache为cache
12
7.2.2.2 三条链表:slabs_full slabs_partial slabs_empty

每个缓存(kmem_cache)都包含了一个 slabs 列表,这是一段连续的内存块(通常都是页面)
其中每个kmem_cache有三条链表:

slabs_full 表示该链表中每个slab的object对象都已经分配完了
slabs_partial 表示该链表中的slab的object对象部分分配完了
slabs_empty  表示该链表中的object对象全部没有分配出去(空 slab,未分配)
123

对象的分配和释放都是在slab中进行的,所以slab可以在三条链表中移动,如果slab中的object都分配完了,则会移到full 链表中;如果分配了一部分object,则会移到partial链表中;如果所有object都释放了,则会移动到empty链表中;其中当系统内存紧张的时候,slabs_empty链表中的slab可能会被返回给系统。

7.2.2.3 cache_cache 结构体等说明

所有的kmem_cache结构都是从cache_cache分配的

static kmem_cache_t cache_cache = {
     slabs_full:     LIST_HEAD_INIT(cache_cache.slabs_full),
     slabs_partial:  LIST_HEAD_INIT(cache_cache.slabs_partial),
     slabs_free:     LIST_HEAD_INIT(cache_cache.slabs_free),
     objsize:        sizeof(kmem_cache_t),
     flags:          SLAB_NO_REAP,
     spinlock:       SPIN_LOCK_UNLOCKED,
     colour_off:     L1_CACHE_BYTES,
     name:           "kmem_cache",
};


struct cache_size{
	size_t cs_size;
	struct kmem_cache *cs_cachep;
}

struct cache_size malloc_sizes[] = { 
	{.cs_size = 32},
	{.cs_size = 64},
	{.cs_size = 128},
	{.cs_size = 256},
	................
	{.cs_size = ~0UL},
};
12345678910111213141516171819202122232425

在系统初始化时,内核会调用kmem_cache_init函数对malloc_size数组进行遍历,对数组中的每个元素都调用kmem_cache_create()函数在cache_cache中分配一个struct kmem_cache 实例,并且把kmem_cache所在的地址赋值给cache_size中的cs_cachep指针

7.2.3 slab

slab 列表中的每个 slab 都是一个连续的内存块(一个或多个连续页),它们被划分成一个个对象(如 mm_struct)。
这些对象是从特定缓存中进行分配和释放的基本元素。

8. slab 和 /proc

proc 文件系统提供了一种简单的方法来监视系统中所有活动的 slab 缓存。
这个文件称为 /proc/slabinfo,
它除了提供一些可以从用户空间访问的可调整参数之外,还提供了有关所有 slab 缓存的详细信息。

如:

[root@localhost home]# cat  /proc/slabinfo
slabinfo - version: 2.1
# name            <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab> : tunables <limit> <batchcount> <sharedfactor> : slabdata <active_slabs> <num_slabs> <sharedavail>
nf_conntrack_ffffffffb58fc900     51     51    320   51    4 : tunables    0    0    0 : slabdata      1      1      0
rpc_inode_cache       51     51    640   51    8 : tunables    0    0    0 : slabdata      1      1      0
xfs_dqtrx              0      0    528   62    8 : tunables    0    0    0 : slabdata      0      0      0
xfs_dquot              0      0    488   67    8 : tunables    0    0    0 : slabdata      0      0      0
xfs_ili             2016   2016    168   48    2 : tunables    0    0    0 : slabdata     42     42      0
xfs_inode          21182  21182    960   34    8 : tunables    0    0    0 : slabdata    623    623      0
xfs_efd_item          39     39    416   39    4 : tunables    0    0    0 : slabdata      1      1      0
xfs_btree_cur         39     39    208   39    2 : tunables    0    0    0 : slabdata      1      1      0
……
123456789101112

9. slab API

Linux内存管理之slab 2:slab API
https://blog.csdn.net/lqy971966/article/details/119801912

参考:
https://blog.csdn.net/yuzhihui_no1/article/details/47305361

参考:

https://blog.csdn.net/qq_22238021/article/details/80214759
https://zhuanlan.zhihu.com/p/61457076
https://blog.csdn.net/rockrockwu/article/details/79976833
https://www.cnblogs.com/wangzahngjun/p/4977425.html

原文链接:https://blog.csdn.net/lqy971966/article/details/112980005