linux中的 slab/slob/slub

很久很久以bai前:一个叫做Mark Hemment的哥儿们du写了Slab。在接下来的一些年里zhi,其他人对Slab进行了完善dao。一年半以前,SLOB问世了。SLOB的目标是针对嵌入式系统的,主要是适用于那些内存非常有限的系统,比如32MB以下的内存,它不太注重large smp系统,虽然最近在这方面有一些小的改进。几个月之前,SLUB闪亮登场。它基本上属于对Slab的重设计(redesign),但是代码更少,并且能更好的适应large NUMA系统。SLUB被很认为是Slab和Slob的取代者,大概在2.6.24/2.6.25将会被同志们抛弃。而SLUB将是未来Linux Kernel中的首选。
Linux内核真是变化太快了,内存管理这块就是一个好例子。
本来Linux内核只有Slab的,现在好了,Slab多了两个兄弟:Slob和Slub。瞧!这就是内核的命名风格,让你光看名字就糊涂了!这也是我这两天读内核源代码的深刻体会,什么cache啊,cache_cache啊,free_area啊,绕不晕你才怪呢~!
以前搞不懂这三个到底什么关系,为什么要有这三个。今天搜了一下,明白了一些。简单的说:Slab是基础,是最早从Sun OS那引进的;Slub是在Slab上进行的改进,在大型机上表现出色(不知道在普通PC上如何),据说还被IA-64作为默认;而Slob是针对小型系统设计的,当然了,主要是嵌入式。相关文章如下:
Anatomy of the Linux slab allocator
The SLUB allocator
The SLOB allocator
这也正好体现了一个Linux内核开发一贯的思想:提供一种机制,而不是一种策略(Provide mechanism not policy)。

----------------------------------------------------------------------------------------------------------------------------------------------------------------------

小内存的问题算是解决了,但还有一个大内存的问题:用伙伴系统分配10 x 4KB的数据时,会去16 x 4KB的空闲列表里面去找(这样得到的物理内存是连续的),但很有可能系统里面有内存,但是伙伴系统分配不出来,因为他们被分割成小的片段。那么,vmalloc就是要用这些碎片来拼凑出一个大内存,相当于收集一些“边角料”,组装成一个成品后“出售”:

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

1.linux 内核 内存管理 slub算法 (一) 原理

https://blog.csdn.net/lukuen/article/details/6935068

图简单明了

2.宋牧春: 多图详解Linux内存分配器slub

https://blog.csdn.net/juS3Ve/article/details/79124544?utm_source=blogxgwz7

http://www.wowotech.net/memory_management/426.html

1.2结合看

Linux-3.14.12内存管理笔记【SLUB分配算法(1)】

http://blog.chinaunix.net/uid-26859697-id-5472929.html

3.伙伴系统之伙伴系统概述--Linux内存管理(十五)

https://blog.csdn.net/GerryLee93/article/details/106476230/#22-%E6%9C%80%E5%A4%A7%E9%98%B6max_order%E4%B8%8Eforce_max_zoneorder%E9%85%8D%E7%BD%AE%E9%80%89%E9%A1%B9

 

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

我用RH9做主机linux usb器件驱动,因为是高速,所以必须一次传完,循环收会出错,因此要开辟2.56M的内存空间,而用__get_free_pages(GFP_KERNEL,9)最多只能分配2M内存,当用__get_free_pages(GFP_KERNEL,10)准备分配4M时出错,从而不能分配2M以上内存,不知如何解决,请教高手!谢谢


|

那你看看你内核中MAX_ORDER这个宏定义是多少(include/linux/mmzone.h)!
在2.6.10的kernel中,是这样定义的

#ifndef CONFIG_FORCE_MAX_ZONEORDER

#define MAX_ORDER 11

#else

#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER

#endif



看看你的多少,贴出来,你可以试着修改该宏定义大一些,满足你的需要!

Good Luck!


|

可以考虑下vmalloc()函数,不过该函数分配的内存物理上是不连续的,所以不能用来做DMA之类的操作!
具体使用,自己看内核的例子!

另外对于__get_free_pages()能分配的最大的内存,是可以通过重新配置,编译内核来扩大的! 具体配置选项好像是
CONFIG_FORCE_MAX_ZONEORDER!如果内核是你自己可以控制的,建议重编译内核,然后用__get_free_pages()这个函数!

Good Luck!
 


|

2.4内核,order的最大值是9.
2.6内核,order的最大值是10或11,依赖于平台.


|

要做DMA操作吗? 如果不是的话,可以用vmalloc


|

linux 技术交流群 8709932 欢迎大家的加入


|

up


|

另外,vmalloc你试过没有,也许可以满足你的需求!


|

因此要开辟2.56M的内存空间

=============================

是你自己写驱动,还是移植别人的?一定要那么多page frames吗?

-------------------------------------------------------------------------------------------------------------------------------------------------------------------------

在Linux内核中, kmalloc能够分配的最大连续内存为2的(MAX_ORDER-1)次方个page(参见alloc_pages函数,     "if (unlikely(order >= MAX_ORDER))        return NULL;"), page的大小一般是4K bytes, MAX_ORDER缺省定义为11, 所以如果不修改内核, kmalloc能够分配的最大连续内存一般是4M bytes.

内核中获取4M以上大内存的方法有三种:

  1.修改MAX_ORDER, 重新编译内核 (CONFIG_FORCE_MAX_ZONEORDER=13)

  2.内核启动选型传递"mem="参数, 如"mem=80M", 预留部分内存; 然后通过request_mem_regionioremap_nocache将预留的内存映射到模块中. 需要修改内核启动参数, 无需重新编译内核. 但这种方法不支持x86架构, 只支持ARM, PowerPC等非x86架构.

(bootargs = "earlycon clk_ignore_unused consoleblank=0 cma=1600M uio_pdrv_genirq.of_id=generic-uio cpuidle.off=1";)

  3.在start_kernel中mem_init函数之前调用alloc_boot_mem函数预分配大块内存, 需要重新编译内核.

 

  在不重新编译内核的前提下, x86架构下内核中只能获取到最大4M的连续内存, 或者使用vmalloc获取4M以上的非连续内存.

 

  而且, 无论是kmalloc还是vmalloc, 分配的内存越大, 失败的可能性越大系统启动后分配内存的时间越早(此时空闲内存越多, 分部也越规律), 成功的可能性越大

------------------------------------------------------------------------------------------------------------------------------------------------------------------------

Linux内核内存管理算法Buddy和Slab

有了前两节的学习相信读者已经知道CPU所有的操作都是建立在虚拟地址上处理(这里的虚拟地址分为内核态虚拟地址和用户态虚拟地址),CPU看到的内存管理都是对page的管理,接下来我们看一下用来管理page的经典算法--Buddy。

 

Buddy分配算法

 

 

 

假设这是一段连续的页框,阴影部分表示已经被使用的页框,现在需要申请一个连续的5个页框。这个时候,在这段内存上不能找到连续的5个空闲的页框,就会去另一段内存上去寻找5个连续的页框,这样子,久而久之就形成了页框的浪费。为了避免出现这种情况,Linux内核中引入了伙伴系统算法(Buddy system)。把所有的空闲页框分组为11个块链表,每个块链表分别包含大小为1,2,4,8,16,32,64,128,256,512和1024个连续页框的页框块。最大可以申请1024个连续页框,对应4MB大小的连续内存。每个页框块的第一个页框的物理地址是该块大小的整数倍,如图:

 

 

 

 

假设要申请一个256个页框的块,先从256个页框的链表中查找空闲块,如果没有,就去512个页框的链表中找,找到了则将页框块分为2个256个页框的块,一个分配给应用,另外一个移到256个页框的链表中。如果512个页框的链表中仍没有空闲块,继续向1024个页框的链表查找,如果仍然没有,则返回错误。页框块在释放时,会主动将两个连续的页框块合并为一个较大的页框块。

从上面可以知道Buddy算法一直在对页框做拆开合并拆开合并的动作。Buddy算法牛逼就牛逼在运用了世界上任何正整数都可以由2^n的和组成。这也是Buddy算法管理空闲页表的本质。 空闲内存的信息我们可以通过以下命令获取:

 

 

也可以通过echo m > /proc/sysrq-trigger来观察buddy状态,与/proc/buddyinfo的信息是一致的:

 

 

 

 

CMA

 

细心的读者或许会发现当Buddy算法对内存拆拆合合的过程中会造成碎片化的现象,以至于内存后来没有了大块的连续内存,全是小块内存。当然这对应用程序是不影响的(前面我们讲过用页表可以把不连续的物理地址在虚拟地址上连续起来),但是内核态就没有办法获取大块连续的内存(比如DMA, Camera, GPU都需要大块物理地址连续的内存)。

在嵌入式设备中一般用CMA来解决上述的问题。CMA的全称是contiguous memory allocator, 其工作原理是:预留一段的内存给驱动使用,但当驱动不用的时候,CMA区域可以分配给用户进程用作匿名内存或者页缓存。而当驱动需要使用时,就将进程占用的内存通过回收或者迁移的方式将之前占用的预留内存腾出来,供驱动使用。

 

Slab

 

在Linux中,伙伴系统(buddy system)是以页为单位管理和分配内存。但是现实的需求却以字节为单位,假如我们需要申请20Bytes,总不能分配一页吧!那岂不是严重浪费内存。那么该如何分配呢?slab分配器就应运而生了,专为小内存分配而生。slab分配器分配内存以Byte为单位。但是slab分配器并没有脱离伙伴系统,而是基于伙伴系统分配的大内存进一步细分成小内存分配。我们先来看一张图:

 

 

 

kmem_cache是一个cache_chain的链表,描述了一个高速缓存,每个高速缓存包含了一个slabs的列表,这通常是一段连续的内存块。存在3种slab:

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

 

slab是slab分配器的最小单位,在实现上一个slab有一个货多个连续的物理页组成(通常只有一页)。单个slab可以在slab链表之间移动,例如如果一个半满slab被分配了对象后变满了,就要从slabs_partial中被删除,同时插入到slabs_full中去。

 

为了进一步解释,这里举个例子来说明,用struct kmem_cache结构描述的一段内存就称作一个slab缓存池。一个slab缓存池就像是一箱牛奶,一箱牛奶中有很多瓶牛奶,每瓶牛奶就是一个object。分配内存的时候,就相当于从牛奶箱中拿一瓶。总有拿完的一天。当箱子空的时候,你就需要去超市再买一箱回来。超市就相当于partial链表,超市存储着很多箱牛奶。如果超市也卖完了,自然就要从厂家进货,然后出售给你。厂家就相当于伙伴系统。

 

可以通过下面命令查看slab缓存的信息:

 

 

 

总结

从内存DDR分为不同的ZONE,到CPU访问的Page通过页表来映射ZONE,再到通过Buddy算法和Slab算法对这些Page进行管理,我们应该可以从感官的角度理解了下图:

 

 

 

用户/内核API名称物理连续?大小限制单位场景
用户空间malloc/calloc/realloc/free 不保证 堆申请 字节calloc初始化为0;realloc改变内存大小。
alloca  栈申请 字节向栈申请内存
mmap/munmap   将文件利用虚拟内存技术映射到内存中去。
brk、sbrk    虚拟内存到内存的映射。sbrk(0)返回program break地址,sbrk调整对的大小。

间    

 vmalloc/vfree

虚拟连续

物理不定

 vmalloc区大小限制

 页

VMALLOC区域

可能睡眠,不能从中断上下文中调用,或其他不允许阻塞情况下调用。

VMALLOC区域vmalloc_start~vmalloc_end之间,vmalloc比kmalloc慢,适用于分配大内存。

  slabkmalloc/kcalloc/krealloc/kfree物理连续

64B-4MB

(随slab而变)

 2^order字节

Normal区域

大小有限,不如vmalloc/malloc大。

最大/小值由KMALLOC_MIN_SIZE/KMALLOC_SHIFT_MAX,对应64B/4MB。

从/proc/slabinfo中的kmalloc-xxxx中分配,建立在kmem_cache_create基础之上。

kmem_cache_create物理连续64B-4MB

字节大小,需对齐

Normal区域

便于固定大小数据的频繁分配和释放,分配时从缓存池中获取地址,释放时也不一定真正释放内存。通过slab进行管理。

伙伴系统 __get_free_page/__get_free_pages物理连续 4MB(1024页)

Normal区域

 __get_free_pages基于alloc_pages,但是限定不能使用HIGHMEM。
 alloc_page/alloc_pages/free_pages物理连续4MB 

Normal/Vmalloc都可 

 CONFIG_FORCE_MAX_ZONEORDER定义了最大页面数2^11,一次能分配到的最大页面数是1024。
  • 2
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值