Detecting Memory Leaks in Kernel

本文介绍了一种在Linux内核中检测内存泄漏的方法,利用kmemleak工具可以在内核模块中找出未释放的内存分配。文章详细描述了如何配置内核支持kmemleak、如何构建和安装内核以及如何通过示例代码演示内存泄漏的检测过程。

 

This doc is from

http://psankar.blogspot.com/2010/11/detecting-memory-leaks-in-kernel.html

 

 

A memory leak is a behavior of a program when it consumes memory but never releases it. In user-space, these days, new applications are written mostly in sophisticated, evolved, modern programming languages like C#, Java etc. This releases the burden of memory management from the programmer. Programmers need to manage memory themselves, only when they code in pre-historic programming languages like C ;-) There are nice tools (like Valgrind) that can detect memory leaks, if they happen in user-space. Valgrind won't work in Kernel space. 

The Linux kernel is predominantly written in C, so that programmers can stay close to the hardware. Also there were no large-scale managed languages at the time the project was started. 

There are some interesting projects that help in writing FUSE filesystems via Mono/C# etc. However your grumpy blogger didn't find them to be active and for all practical purposes, C is the default Kernel programming language. 

Few days back, I was tasked with detecting memory leaks in a legacy kernel module that is not in Linus' tree. Code that goes into Linus' tree is usually of high quality and won't have memory leaks because of the rigorous reviews that are performed in LKML. So, any kernel code that is not merged upstream has a high chance of having leaks, among other bad things (so upstream your code, NOW). The simple tutorial below will explain how to detect memory leaks in kernel modules.

kmemleak

There is a nice tool named 'kmemleak' available in the Linux kernel since 2.6.31 to detect memory leaks. This tool is claimed to report a few false positives but that should not stop someone from using it. I was trying to find a kernel-space-leak-detector but did not find any links via Google. Some kernel hackers told me about this tool over IRC and this post is more as a pointer to the "kmemleak" docs, when somone googles for "kernel memory leak detection".

Pre-requisites: 
+ You need to know how to build and install your own kernel. 
+ You need to have 2.6.31 or newer version of the linux kernel
+ It is good if you know how to compile a kernel module. But don't worry if you don't know. You can refer to my previous tutorial for a simple hello-world kernel module. 

So, without further ado, the steps are: 

Step 1: Compile kernel with "CONFIG_DEBUG_KMEMLEAK" option enabled. You can get to this option via: make menuconfig, "Kernel Hacking", "Kernel Memory Leak Detector" , while compiling your kernel. 

Step 2: Increase the config option "Maximum kmemleak early log entires" value to a sufficiently large number like 1200. The default value of 400 may not work correctly in all configurations.

Step 3: Install this kernel and Reboot to this newly configured kernel. Do not be alarmed if your machine is slow.

Step 4: Upon reboot, Check if your debugfs is mounted. Otherwise mount it. If all is well, you should see a file kmemleak under your debugfs mounted location.

mount -t debugfs nodev /sys/kernel/debug/
cat /sys/kernel/debug/kmemleak

The above /sys/kernel/debug/kmemleak file will contain information about any memory leak that has been detected so far since the machine booted. Ideally there should be none, until this point in time. 

Step 5: Now we will see how we can detect a memory leak in a dummy kernel module as follows. Write a dummy kernel module with the following source (hello.c):


#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/vmalloc.h>

/* Never write a function like this ;) */
void myfunc(void)
{
        char *ptr;
        ptr = vmalloc(512);
        ptr = vmalloc(512);
        ptr = vmalloc(512);
}

int hello_init(void)
{
        printk(KERN_ALERT "Hello World");
        myfunc();
        return 0;
}

static void hello_exit(void)
{
        printk(KERN_ALERT "Goodbye World");
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_LICENSE("GPL v2");
MODULE_AUTHOR("Your Name");


Now the most important line in the above code snippet is:
ptr = vmalloc(512);
We allocate memory, as above, in the kernel module but never free this memory. 

Step 6: vi Makefile
EXTRA_CFLAGS=-g
obj-m := hello-kernel.o
hello-kernel-objs := hello.o

Step 7: Generate the kernel-object file hello-kernel.ko in your current directory:
make -C /lib/modules/`uname -r`/build M=`pwd`

All commands from now on require root permission.

Step 7.5: [Optional] At any stage, if you want to clear the memory profiler output so far created, so that we can focus just on the leaks reported from then on, you can do:
echo clear > /sys/kernel/debug/kmemleak

Step 8: Now insert the kernel object
insmod hello-kernel.ko

Step 9: The memory leak detection thread runs periodically. If you want to perform a test at any instant you want, Do:
echo scan > /sys/kernel/debug/kmemleak

Step 10: Now we will check if the leak is detected. Do:
cat /sys/kernel/debug/kmemleak
You should see:

unreferenced object 0xf9061000 (size 512):
comm "insmod", pid 12750, jiffies 14401507 (age 110.217s)
hex dump (first 32 bytes):
1c 0f 00 00 01 12 00 00 2a 0f 00 00 01 12 00 00 ........*.......
38 0f 00 00 01 12 00 00 bc 0f 00 00 01 12 00 00 8...............
backtrace:
[< c10b0001>] create_object+0x114/0x1db
[< c148b4d0>] kmemleak_alloc+0x21/0x3f
[< c10a43e9>] __vmalloc_node+0x83/0x90
[< c10a44b9>] vmalloc+0x1c/0x1e
[< f9055021>] myfunc+0x21/0x23 [hello_kernel]
[< f9058012>] 0xf9058012
[< c1001226>] do_one_initcall+0x71/0x113
[< c1056c48>] sys_init_module+0x1241/0x1430
[< c100284c>] sysenter_do_call+0x12/0x22
[< ffffffff>] 0xffffffff


As you can see in the bold text above, the leak is detected in myfunc function.

Caution: The memory leak detector code may take some time to identify the leaks. So repeat steps 9 and 10, after few minutes, if you don't get the leaks reported first time. You can try to kiss your hand elbow to pass time meanwhile ;-)

Further Reading
+ LWN Article about kmemleak - http://lwn.net/Articles/187979/ 
+ Under Kernel sources directory: Documentation/kmemleak.txt

Thanks a lot to Catalin Marinas for kmemleak and the people at kernelnewbies for helping, not just for this problem but for nuuuumerous people.

<think>我们有一个结构体定义,其中新增了一个int类型的成员leak_confirmed。在C语言中,结构体数组在静态初始化时,对于未显式初始化的成员,会根据初始化规则进行初始化。 具体规则是:如果初始化列表中对某个成员没有显式初始化,那么: - 对于静态存储期(static storage duration)的对象(本例中process_table是全局静态数组,所以具有静态存储期),未显式初始化的成员会被初始化为0(整数为0,指针为NULL等)。 - 自动存储期的局部变量如果不显式初始化,其值是不确定的,但这里我们的数组是全局的(或静态的),所以适用静态存储期规则。 因此,在您提供的初始化列表中,没有为leak_confirmed指定值,那么它会被自动初始化为0。 但是,请注意您的初始化列表写法: static PROCESS process_table[] = { {"cos", -1, "/bin/cos", CORE, {NULL}, -1, 0}, ... // 其他类似 }; 在初始化每个结构体元素时,您使用了位置初始化(按成员顺序),但注意初始化列表中的初始化器数量少于结构体的成员数量。根据C标准,剩余的成员(包括新加的leak_confirmed)将被初始化为0(对于静态存储期的对象)。 所以,leak_confirmed会被自动初始化为0。 但是,这里有一个细节:您的初始化列表中,每个结构体元素初始化器内的初始化器个数并不相同(因为最后一个元素初始化后还有逗号,但这是允许的)。另外,每个初始化器内,实际上只初始化了前面的7个成员(从name到fail_cnt),后面的所有成员(包括内存泄漏相关的成员和新加的leak_confirmed)都没有显式初始化。 因此,在静态初始化的过程中,这些未显式初始化的成员都会被初始化为0(或0.0,NULL等)。 结论:新添加的leak_confirmed在初始化时会被自动设置为0。 注意:如果结构体数组不是静态存储期(例如是局部变量且没有static关键字),那么未初始化的成员的值是未定义的(垃圾值)。但您这里是全局静态数组,所以没问题。 §§相关问题§§ 1. 对于具有静态存储期的结构体数组,未显式初始化的成员是否总是被初始化为0? 2. 如果结构体中新增一个指针成员,在静态初始化时未显式初始化,它会被自动初始化为NULL吗? 3. 如何在C语言中为结构体成员设置非零的默认初始值(特别是当结构体数组庞大时)? 4. 如何验证结构体成员在静态初始化时确实被初始化为0(例如使用调试器或打印)? 5. 如果结构体数组改为动态分配(例如malloc),如何确保所有成员被正确初始化为0? </think>是的,新增的 `leak_confirmed` 成员在初始化时会自动被设为 **0**。原因如下: ### 初始化规则解析: 1. **静态存储期的默认初始化** 您的 `process_table` 是全局静态数组(`static PROCESS process_table[]`),根据 C 标准: - **未显式初始化的成员**会自动初始化为 **0 值** - **整数类型**(如 `int leak_confirmed`)会被设为 `0` - **浮点类型**设为 `0.0`,**指针**设为 `NULL` 2. **初始化列表行为** 当前初始化列表仅覆盖前 7 个成员: ```c {"cos", -1, "/bin/cos", CORE, {NULL}, -1, 0} // 显式初始化 name 到 fail_cnt ``` 所有后续成员(包括 `samples`、`leak_count` 和新加的 `leak_confirmed`)因未显式指定值,均被编译器设为 **0**。 3. **验证示例** 若打印初始化后的结构体: ```c printf("leak_confirmed: %d\n", process_table[0].leak_confirmed); // 输出 0 ``` ### 关键注意事项: - ✅ **安全依赖**:静态/全局变量的零初始化是 C 标准行为(C99/C11
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值