内核泄露检测(kmemleak)
1 介绍:
Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中。
2 用法:
在Kernel hacking里, CONFIG_DEBUG_KMEMLEAK 这样被使得生效, 有一个内核线程进行内存扫描, 间隔时间是每10分钟也就是那默认值时候, 去打印所发现的新的未曾被引用的对象的数量, 并最终完成这一行为。
查看内核打印信息详细过程如下:
1、挂载debugfs文件系统
将文件系统类型指定为debugfs, 挂载没有设备的节点, 挂载到系统内核调试的相关路径处, 即/sys/kernel/debug/。
2、开启内核自动检测线程
把“echo scan”, 运用到“输入至”, “/sys/kernel/debug/kmemleak”这个路径上。
3、查看打印信息
把, 系统的, 内核的, 调试的, 那个名为kmemleak的文件内容, 用cat命令查看。
4、在将写在 /sys/kernel/debug/kmemleak 里全新的内存遗漏禀报重新予以书写之时, 旧有的内核核查报告单会被清除掉。
往, /sys/kernel/debug/kmemleak, 这个路径里, 写入这一行内容, echo clear, 以此来实现对应操作。
内存扫描参数能被修改哟, 途径是朝着/sys/kernel/debug/kmemleak 文件进行写入操作。至于参数会有如此使用方法:
off 禁用kmemleak(不可逆)
stack=on 启用任务堆栈扫描(default)
stack=off 禁用任务堆栈扫描
scan=on 启动自动记忆扫描线程(default)
scan=off 停止自动记忆扫描线程
scan= 设置n秒内自动记忆扫描,默认600s
scan 开启内核扫描
clear 清除内存泄露报告
dump= 转存信息对象在
借由“kmemleak = OFF”这一方式, 还能够于启动之际在内核命令行中将Kmemleak予以禁用。在开始初始化成kmemleak此前, 内存施行分配或者释放那些有关运作, 都被留存于一段前期留存的日志缓冲区之中。此缓冲区具体的大小, 则借由配给CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE来进行相应设置。
3 功能实现的基本方法原理
经由kmalloc进行的内存分配, 还有借助vmalloc执行的内存分配, 以及运用kmem_cache_alloc开展的内存分配, 都会对其分配出来的内存区域展开跟踪, 并且会和其他方面一同。
的分配大小和堆栈跟踪信息,存储在PRIO搜索树。
调用跟踪这一相应的释放函数, 与此同时指针便会, 即从kmemleak数据结构里得以移除。
被分配的内存块, 被视作是独立的, 要是没有指针指向它起始的地址, 或者块内部的任何一处位置, 可以发觉扫描内存, 其中涵盖已保存的寄存器。这表明, 存在可能没有办法, 让内核经由所分配的地址传递块给一个释放函数, 所以, 该块被认定为是一个内存泄漏。
扫描算法步骤:
1。标记的所有分配对象为白色(稍后将剩余的白色物体
考虑独立的)
2。扫描存储器, 从所述数据片段和栈开始, 检查地址的值存储情况。该值存储于PRIO搜索树之中。如果。
一个白色的对象的指针被发现,该对象将被添加到黑名单
3。扫描出来的呈灰色的对象所匹配到的地址, 当中一些白色的物体是可以转变为黑色的, 并且要添加结束时候的黑名单, 一直持续到黑色集合结束。
4。被留下的白色的物体被当作是独立儿, 随后相关情况被写入/sys/kernel/debug/kmemleak, 同时进行了报告。
一些被分配的用作内存块的指针, 存在于内核的内部数据结构当中, 且它们无法被检测成为孤儿, 对。
若要避免这种情况, kmemleak能够存储的数量的值, 会指向一个。
内的块的地址范围内的地址,需要找到使
块, 这并不被视作泄漏呢, 其中有着一个范例, 它是运用vmalloc()函数的情况。
4 相关Kmemleak API
------------
在include / linux / kmemleak.h里头, 看着这样的函数原型所处的那个头。
kmemleak_init - 初始化kmemleak
kmemleak_alloc - 一个内存块分配的通知
以逐CPU的方式进行内存块的分配, 对此分配进行通知, 借助kmemleak_alloc_percpu。
kmemleak_free - 通知的内存块释放
将部分内存块已经被通知释放这件事借助kmemleak_free_part来表述。
内存块针对每个CPU进行释放时的通知借助kmemleak_free_percpu达成, 这是一种特定的通知方式。
对于并非泄密的状况下, 对相关对象予以标记, 此为kmemleak_not_leak。
针对kmemleak_ignore而言, 就是在出现泄漏状况的时候, 不会去进行扫描,也不会报告该对象。
检测内存泄漏扫描区域那里, 增添内存块在进行添加操作, 针对内存块于相应区域给予添加处理, 以此来操作添加扫描区域里位于其中的内存块。
kmemleak_no_scan - 不扫描的内存块
kmemleak_erase - 删除一个指针变量的旧值
一个被称作kmemleak_alloc_recursive的东西, 它是专门针对kmemleak_alloc的, 其作用仅仅是进行递归检查。
那用于kmemleak_free的kmemleak_free_recursive, 仅仅是做着重检查递归的操作。
5 处理假阳性/阴性
请你提供一下具体可以改写的句子呀, 这样我才能按照要求进行改写。
之所以对于那种属于假性的内存泄漏, 且是不需要去报告的情况, 是因为在值的内存扫描进程当中发现kmemleak是指向这般的对象。而为了达成减少假性报告数量的目的, kmemleak提供了kmemleak_。
忽略, 内存泄漏扫描区域, 内存泄漏不进行扫描, 内存泄漏擦除这般几个东西的作用, 涵盖按给定方式指令指针去开展扫描动作这一点能实现该功能, 而平常默认状态下它们执行的这项有查找泄露问题性质的扫描操作是不开启的。
当遇到不能够确定是不是内存泄露这种情况时, kmemleak会拿出kmemleak_not_leak。kmemleak_ignore具备指定固定下类型的的数据有没有必要去扫描以及打印的功能, 对上面提及的具体函数进行分析, 仔细地看见3.3详细处理当中的详情以及功能函数的分析结果。
存在一些泄露仅发生于瞬间之时, 特别是在SMP系统情况之中, 这是由于指向的指针会暂且被储存于CPU的寄存器或者栈里面, 当出现了内存泄露这种状况的时候, Kmemleak会去定义MSECS_MIN_AGE, 这项内容存在着个默认值为1000的情况, 说的是有关一个对象的最低时间范畴。
6 限制和缺点
-------------------------
主要存在的缺点, 是致使内存分配有所减少, 以及性能释放受到影响。为了防止出现其他方面的开销, 仅仅开展内存扫描操作, 当处于对/ sys /kernel/debug/ kmemleak文件进行读取的状态时。无论如何, 这个工具是被用于调试方面的目的, 它所展现出来的性能并非是重要的。为了让算法保持单纯简易, kmemleak的值指向处于任何扫描一个块的地址范围之内的地址。这有可能会造成假阴性报告数量的增加。然而, 它涵盖了真正的内存泄漏情况, 最终内存泄露将会变得能够被看见。
假阴性的另一个来源是数据存储在非指针值。
于未来版本当中, kmemleak仅能够对指针成员里所分配的结构予以扫描, 该功能将诸多上述假阴性情况给化解掉啦。
该工具兴许会有误报情形存在, 这些个案的分配块说不定不需要予以释放(像是某些处于 init_call 功能之际的情况), 如此这般的指针借由别的办法来计算, 跟通常的 container_of 宏或者指针被存放于一处相对比, 不会被 kmemleak 加以扫描, 页分配以及 ioremap 是不会被跟踪的。
7 测试的特定部分kmemleak
请你提供需要改写的句子, 不然没办法按照要求进行操作。
初始启动之际, /sys/kernel/debug/kmemleak输出页面数量较多。处于这般情形下, 检测指定已开发代码错误之时, 能够借助清除/sys/kerner/debug/kmemleak的输出。经过启动kmemleak的扫描后, 能够寻觅到新的未引用对象, 这理应与测试特定的代码段相关联。
详细步骤如下:
要测试的关键部分之前需要清除kmemleak报告:
echo clear > /sys/kernel/debug/kmemleak
测试你的内核或模块...
回声扫描等于5, 被重定向到, /系统/内核/调试/内核内存泄漏处。
然后像往常一样查看报告:
cat /sys/kernel/debug/kmemleak

详见经过测试得以确定的实例的, 是内核文档kmenleak_test.txt这个文档。
1:检测内核内存泄漏的功能
2:Documentation/kmemleak.txt
3:内核demo:mm/kmemleak-test.c
对于kmemleak,需要理解下面三点就可以了
我们要晓得, 它能够检测哪一些内存泄漏, 也就是,通过什么样的方法分配的内存, 是可以被检测的。
要问的是, 内核情况特殊, 所指为何? 特指分配内存却无引用, 针对这种情况, 要防止kmemleak report, 该用啥方法呀?
检测的机理具体是什么, 怎样才能够知晓所分配的内存到底有没有被引用。
关注点1
kmalloc/kzalloc
vmalloc
kmem_cache_alloc
per_cpu
关注点2
“”而不漏的kmemleak_not_leak, 会被忽略的kmemleak_ignore, 不进行扫描的kmemleak_no_scan。
那几个函数于内核里遭使用, 其目的在于不被kmemleak打印出来, 然而深层次的区别究竟是什么呢?
kmemleak_not_leak
/**
使内存泄漏检测标记为正常的已分配对象, 这并非内存泄漏, 标记一个已分配的对象, 将其视为误报。
*/
不进行打印, 然而却要扫描由这个指针所分配的内存的内容, 若分配了数据结构, 那么该结构自身不打印, 不过会扫描该结构里面的成员变量, 看其是否引用了其他指针。
该函数常常派上用场时存在这一状况: 那就是曾被用于分配内存的内存,永远都不会迎来被释放的时刻, 其相关关联到内核, 属于vmlinux这种类型, 再不然就是不可施行移除操作的模块这一类别。
kmemleak_ignore
/**
一个已然被分配出来的对象, 就此予以忽略, 此操作名为kmemleak_ignore。
* @ptr: pointer to beginning of the object
*/
没有操作是针对专门用于打印, 且专门用于扫描指针所指向的数据结构的成员变量。有这样一种知晓存在, 那就是所分配得来的数据结构里面不存在其他形式的引用, 排除指针。
kmemleak_no_scan
/**
不扫描已分配对象的-kmemleak_no_scan, , , 逗号, , 且带有换行符的。
* @ptr: pointer to beginning of the object
在已知给定对象不含有任何东西的情况之处, 这一情况是知晓的, 该对象是已知的, 并没有任何东西被其包含。
*/
该指针本身被扫描,但是内容不会扫描。
关注点3
所说的reference, 也就是所分配的内存存在指针引用, 要是不存在任何指针引用, 那决然就是memleak。
所以, 要去查找, 所有指针的内容, 以此来寻找, 其内容是不是包含, 我们已经记录的, 分配内存的地址, 这个地址范围是, 在其实地址加上size之间。
那么这些指针变量的
1:函数的局部变量
这些变量本身在栈中,所以需要检测进程的内核栈
2:全局变量(整个系统/模块内)静态变量
这些变量是存在:ELF的bss/data
可以经由查看vmlinux , 或查看*.ko , 来看找到这些变量 , 进而查看这种指针变量的区段。
可以通过 objdump -x file
---指针是静态分配
3: 指针自身是通过动态方式进行分配的, 也就是说, 是对内存块(此内存块为struct类型)实施动态分配操作, 其成员变量属于指针。
所以必须要搜索这类动态分配的内存块的内容。
通过objdump -x vmlinux
.data
鉴于全局表、变量等所处位置, 利用objdump -s -j .data .process.o 将对其进行十六进制转储。
.bss
不要在你的文件中寻找.bss部分: 没有这样的部分。那是你未初始化的数组和变量所在的地方, 并且加载器“知道”它们应该用零填充……在你的磁盘上存储比已经存在的更多的零没有意义, 不是吗? 不要去在你的那个文件里头找寻.bss的那些片段: 根本就不存在这样子的片段。那是你那些未初始化的数组以及变量所处的位置, 而且加载器“晓得” 它们应当要用零去进行填充…… 在你的磁盘之上存储相较于早已存在的情况下数量更多的零全然没有任何意义, 对不对呀?
.rodata
.data..percpu
具体的扫描实现 kmemleak_scan()
data/bss 段扫描
/* data/bss scanning */
扫描块, 将起始数据送去, 结束数据送去, 空指针传送, 次数为1进行操作。
针对__bss_start此起始点, 对__bss_stop此终结点进行扫描之操作, 以NULL作为相关参数, 操作次数设定为1次 , 执行scan_block函数。
data..percpu
#ifdef CONFIG_SMP
关于每个CPU相关部分展开的仔细扫查 , /* per-cpu sections scanning */。
for_each_possible_cpu(i)
扫描块。从(每CPU起始位置 加 每CPU偏移量(i))这儿开始扫描。
每次处理单个CPU结束后加上基于每个CPU计算的偏移量(此处具体关联到索引为i的元素), 没有其他指向, 数量为1。