转自:http://blog.csdn.net/lishenglong666/article/details/8287783
版权声明:本文为博主原创文章,未经博主允许不得转载。
内核泄露检测(kmemleak)
介绍:
Kmemleak 提供了一种可选的内核泄漏检测,其方法类似于跟踪内存收集器。(http://en.wikipedia.org/wiki/Garbage_collection_%28computer_science%29#Tracing_garbage_collectors)当独立的对象没有被释放时,其报告记录在 /sys/kernel/debug/kmemleak中。
用法:
CONFIG_DEBUG_KMEMLEAK 在Kernel hacking中被使能,一个内核线程每10分钟(默认值)扫描内存,并打印发现新的未引用的对象的数量。
查看内核打印信息详细过程如下:
1、挂载debugfs文件系统
mount -t debugfs nodev /sys/kernel/debug/
2、开启内核自动检测线程
echo scan > /sys/kernel/debug/kmemleak
3、查看打印信息
cat /sys/kernel/debug/kmemleak
4、清除内核检测报告,新的内存泄露报告将重新写入/sys/kernel/debug/kmemleak
echo clear > /sys/kernel/debug/kmemleak
内存扫描参数可以进行修改通过向/sys/kernel/debug/kmemleak 文件写入。 参数使用如下:
off 禁用kmemleak(不可逆)
stack=on 启用任务堆栈扫描(default)
stack=off 禁用任务堆栈扫描
scan=on 启动自动记忆扫描线程(default)
scan=off 停止自动记忆扫描线程
scan=<secs> 设置n秒内自动记忆扫描,默认600s
scan 开启内核扫描
clear 清除内存泄露报告
dump=<addr> 转存信息对象在<addr>
通过“kmemleak = OFF”,也可以在启动时禁用Kmemleak在内核命令行。在初始化kmemleak之前,内存的分配或释放这些动作被存储在一个前期日志缓冲区。这个缓冲区的大小通过配CONFIG_DEBUG_KMEMLEAK_EARLY_LOG_SIZE设置。
功能实现的基本方法原理
通过的kmalloc、vmalloc、kmem_cache_alloc等内存分配会跟踪其指针,连同其他
的分配大小和堆栈跟踪信息,存储在PRIO搜索树。
相应的释放函数调用跟踪和指针就会从kmemleak数据结构中移除。
分配的内存块,被认为是独立的,如果没有指针指向它起始地址或块的内部的任何位置,可以发现扫描内存(包括已保存的寄存器)。这意味着,有可能没有办法为内核通过所分配的地址传递块到一个释放函数,因此,该块被认为是一个内存泄漏。
扫描算法步骤:
1。标记的所有分配对象为白色(稍后将剩余的白色物体
考虑独立的)
2。扫描存储器与所述数据片段和栈开始,检查对地址的值存储在PRIO搜索树。如果
一个白色的对象的指针被发现,该对象将被添加到黑名单
3。扫描的灰色对象匹配的地址(一些白色物体可以变成黑色,并添加结束时的黑名单),直到黑色集结束
4。剩下的白色物体被认为是独立儿,并报告写入/sys/kernel/debug/kmemleak。
一些分配的内存块的指针在内核的内部数据结构和它们不能被检测为孤儿。对
避免这种情况,kmemleak也可以存储的数量的值,指向一个
内的块的地址范围内的地址,需要找到使
块不被认为是泄漏。其中一个例子是使用vmalloc()函数。
Kmemleak API
------------
见include / linux / kmemleak.h中的函数原型的头。
kmemleak_init - 初始化kmemleak
kmemleak_alloc - 一个内存块分配的通知
kmemleak_alloc_percpu - 通知的一个percpu的内存块分配
kmemleak_free - 通知的内存块释放
kmemleak_free_part - 通知释放部分内存块
kmemleak_free_percpu - 一个percpu内存块释放的通知
kmemleak_not_leak - 当不是泄露时,标记对象
kmemleak_ignore - 当泄漏时不扫描或报告对象
kmemleak_scan_area - 添加扫描区域内的内存块
kmemleak_no_scan - 不扫描的内存块
kmemleak_erase - 删除一个指针变量的旧值
kmemleak_alloc_recursive - 为kmemleak_alloc,只检查递归
kmemleak_free_recursive - 为kmemleak_free,只检查递归
处理假阳性/阴性
--------------------------------------
对于假性的内存泄漏,但不需要报告的,由于值的内存扫描过程中发现kmemleak是指向这样的对象。为了减少假性报告的数目,kmemleak提供kmemleak_
ignore,kmemleak_scan_area,kmemleak_no_scan,kmemleak_erase的功能,可以指定指针扫描方式,他们的扫描默认情况下不启用。
对于不能确定是否是内存泄露的,kmemleak提供kmemleak_not_leak。kmemleak_ignore的功能可以指定固定类型的数据是否需要扫描或打印,以上具体函数分析详见3.3详细处理处理过程及功能函数分析。
有的泄露只是瞬间的,尤其是在SMP系统,因为指针暂时存储在CPU的寄存器或栈。当内存泄漏时Kmemleak定义MSECS_MIN_AGE(默认为1000)一个对象的最低时间。
限制和缺点
-------------------------
主要缺点是减少了内存分配和性能释放。为了避免其他开销,只进行内存扫描,当在/ sys /kernel/debug/ kmemleak文件被读取。不管怎样,这个工具是用于调试目的,其表现的性能不是重要的。为了保持算法简单,kmemleak的值指向任何扫描一个块的地址范围内的地址。这可能会导致增加假阴性的报告。然而,它包括真正的内存泄漏,最终内存泄露将变得可见。
假阴性的另一个来源是数据存储在非指针值。
在未来的版本中,kmemleak只能扫描指针成员中分配的结构。此功能解决了许多上述假阴性的情况下。
该工具可能存在误报。这些个案的分配块可能不需要被释放(如一些在init_call功能的情况下),这样的指针通过其他方法计算,与通常的container_of宏或指针被存储在一个位置相比不会被kmemleak扫描。页分配和ioremap不被跟踪
测试的特定部分kmemleak
---------------------------------------
在初始启动时,/sys/kernel/debug/kmemleak输出页面比较多。这样的情况下,当检测指定已经开发的代码错误时,可以通过清除/sys/kerner/debug/kmemleak的输出。通过启动kmemleak的扫描后,你可以找到新的未引用的对象,这应该与测试特定的代码段。
详细步骤如下:
要测试的关键部分之前需要清除kmemleak报告:
echo clear > /sys/kernel/debug/kmemleak
测试你的内核或模块...
echo scan =5> /sys/kernel/debug/kmemleak
然后像往常一样查看报告:
cat /sys/kernel/debug/kmemleak
已经测试的实例详见内核文档kmenleak_test.txt文档
1:检测内核内存泄漏的功能
- 关注点1
- 关注点2
* kmemleak_not_leak - mark an allocated object as false positive
* @ptr: pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to no longer
* be reported as leak and always be scanned.
*/
* kmemleak_ignore - ignore an allocated object
* @ptr: pointer to beginning of the object
*
* Calling this function on an object will cause the memory block to be
* ignored (not scanned and not reported as a leak). This is usually done when
* it is known that the corresponding block is not a leak and does not contain
* any references to other allocated memory blocks.
*/
* kmemleak_no_scan - do not scan an allocated object
* @ptr: pointer to beginning of the object
*
* This function notifies kmemleak not to scan the given memory block. Useful
* in situations where it is known that the given object does not contain any
* references to other objects. Kmemleak will not scan such objects reducing
* the number of false negatives.
*/
- 关注点3
that's where your strings go, usually the things you forgot when linking and that cause your kernel not to work. objdump -s -j .rodata .process.o will hexdump it. Note that depending on the compiler, you may have more sections like this. |
- kmemleak_scan()
scan_block(__bss_start, __bss_stop, NULL, 1);
/* per-cpu sections scanning */
for_each_possible_cpu(i)
scan_block(__per_cpu_start + per_cpu_offset(i),
__per_cpu_end + per_cpu_offset(i), NULL, 1);
#endif
* Struct page scanning for each node.
*/
lock_memory_hotplug();
for_each_online_node(i) {
pg_data_t *pgdat = NODE_DATA(i);
unsigned long start_pfn = pgdat->node_start_pfn;
unsigned long end_pfn = start_pfn + pgdat->node_spanned_pages;
unsigned long pfn;
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
struct page *page;
if (!pfn_valid(pfn))
continue;
page = pfn_to_page(pfn);
/* only scan if page is in use */
if (page_count(page) == 0)
continue;
scan_block(page, page + 1, NULL, 1);
}
}
unlock_memory_hotplug();
struct task_struct *p, *g;
read_lock(&tasklist_lock);
do_each_thread(g, p) {
scan_block(task_stack_page(p), task_stack_page(p) +
THREAD_SIZE, NULL, 0);
} while_each_thread(g, p);
read_unlock(&tasklist_lock);
- 问题