
那么如果程序请求1MB的内存,那么内存分配 *** 作逻辑如下:
这个算法就是所谓的 binary buddy 分配算法。
在 Linux 中,这个二分法最小分割到 4096 字节,也就是一个页的大小。
因此总共有 11 种大小,分别为 4KB,8KB,……4MB。
其中 4KB 为 order 0,4MB 为 order 10.
我们称其 max order 为 12,有些资料会提到这个概念。
以上这些信息可以在 /proc/buddyinfo 上查看,其格式大概是这样:
buddy 在上面这种情况下,有些被分为小块内存,那么就会存在内存碎片的问题。
/proc/pagetypeinfo
以上 buddy 管理的是不小于4K 的内存分配,slab 则是管理小于4KB 的内存对象。
采用伙伴算法分配内存时,每次至少分配一个页面。但当请求分配的内存大小为几十个字节或几百个字节时应该如何处理?如何在一个页面中分配小的内存区,小内存区的分配所产生的内碎片又如何解决?Linux2.0采用的解决办法是建立了13个空闲区链表,它们的大小从32字节到132056字节。从Linux2.2开始,MM的开发者采用了一种叫做slab的分配模式,该模式早在1994年就被开发出来,用于Sun Microsystem Solaris 2.4 *** 作系统中。Slab的提出主要是基于以下考虑:
· 内核对内存区的分配取决于所存放数据的类型。例如,当给用户态进程分配页面时,内核调用get_free_page()函数,并用0填充这个页面。 而给内核的数据结构分配页面时,事情没有这么简单,例如,要对数据结构所在的内存进行初始化、在不用时要收回它们所占用的内存。因此,Slab中引入了对象这个概念,所谓对象就是存放一组数据结构的内存区,其方法就是构造或析构函数,构造函数用于初始化数据结构所在的内存区,而析构函数收回相应的内存区。但为了便于理解,你也可以把对象直接看作内核的数据结构。为了避免重复初始化对象,Slab分配模式并不丢弃已分配的对象,而是释放但把它们依然保留在内存中。当以后又要请求分配同一对象时,就可以从内存获取而不用进行初始化,这是在Solaris 中引入Slab的基本思想。
实际上,Linux中对Slab分配模式有所改进,它对内存区的处理并不需要进行初始化或回收。出于效率的考虑,Linux并不调用对象的构造或析构函数,而是把指向这两个函数的指针都置为空。Linux中引入Slab的主要目的是为了减少对伙伴算法的调用次数。
· 实际上,内核经常反复使用某一内存区。例如,只要内核创建一个新的进程,就要为该进程相关的数据结构(task_struct、打开文件对象等)分配内存区。当进程结束时,收回这些内存区。因为进程的创建和撤销非常频繁,因此,Linux的早期版本把大量的时间花费在反复分配或回收这些内存区上。从Linux2.2开始,把那些频繁使用的页面保存在高速缓存中并重新使用。
· 可以根据对内存区的使用频率来对它分类。对于预期频繁使用的内存区,可以创建一组特定大小的专用缓冲区进行处理,以避免内碎片的产生。对于较少使用的内存区,可以创建一组通用缓冲区(如Linux2.0中所使用的2的幂次方)来处理,即使这种处理模式产生碎片,也对整个系统的性能影响不大。
· 硬件高速缓存的使用,又为尽量减少对伙伴算法的调用提供了另一个理由,因为对伙伴算法的每次调用都会“弄脏”硬件高速缓存,因此,这就增加了对内存的平均访问次数。
Slab分配模式把对象分组放进缓冲区(尽管英文中使用了Cache这个词,但实际上指的是内存中的区域,而不是指硬件高速缓存)。因为缓冲区的组织和管理与硬件高速缓存的命中率密切相关,因此,Slab缓冲区并非由各个对象直接构成,而是由一连串的“大块(Slab)”构成,而每个大块中则包含了若干个同种类型的对象,这些对象或已被分配,或空闲,如图6.12所示。一般而言,对象分两种,一种是大对象,一种是小对象。所谓小对象,是指在一个页面中可以容纳下好几个对象的那种。例如,一个inode结构大约占300多个字节,因此,一个页面中可以容纳8个以上的inode结构,因此,inode结构就为小对象。Linux内核中把小于512字节的对象叫做小对象。
假如通过“Free”查看内存几乎耗尽,但通过 top/ps 命令却看不出来用户态应用程序占用太多的内存空间, 那么内核模块可能发生了内存泄露
SLAB 是Linux内核中按照对象大小进行分配的内存分配器。
通过SLAB的信息来查看内核模块占用的内存空间:
方法1. 查看meminfo文件
方法2. 查看slabinfo文件
一般查看slabinfo文件就足以,如果发现slabinfo中占用内存过大,那基本可以断定,内核模块出现了内存泄露了
还有个命令 slabinfo 也是可以看,其实也是去读 /proc/slabinfo 后可视化出来
Linux内核的Kmemleak实现内存泄露检测
看看下面这个函数是哪里导致的内存泄漏呢?
一眼可能不容易看出上面的有什么问题,有kmalloc,有kfree 成对出现的。
问题正好出在 pr_debug 这个函数中的参数传递, 熟悉函数调用传参的人应该会知道编译器一般对参数的处理采用堆栈的方式,是一个先进后出的过程,这样参数的执行一般是逆序的(由于编译器实现的不同,这个过程不是确定的),这样kfree会在kmalloc之前运行,导致每次运行都会泄漏一点内存。
Resolving Memory Leaks In Linux Kernel
Slab Allocator
Proc Info
Using Crash Debugger
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)