内核内存分配的基础分析
通常,内存泄漏会发生在用户态,比如开发者使用malloc()分配一部分空间后,使用完后没有通过free()进行回收,那么就会出现内存泄漏问题,导致内存占用越来越大。
如果内存泄漏发生在内核或者设备驱动中,可能应用开发者排查起来相对陌生,那么怎么分析内存泄漏是否是内核或者设备驱动导致的呢?
本文将会通过简单的实验测试流程为你分析内核中内核泄漏,通过systemstap等工具进行观察具体的tracepoint,来判断是否发生内核内存分配。
kmalloc和vmalloc
malloc()函数是用户态中的内存分配函数,在内核态中,使用kmalloc和vmalloc函数进行动态内存空间分配。kmalloc和vmalloc的区别是:kmalloc分配的虚拟地址对应的物理空间是连续的,而vmalloc申请的内存对应的物理空间是不连续的。通过vmalloc分配的地址,由于物理空间不连续,所以获得的页需要逐个进行映射,相比之下效率不高,一般是在获取大内存的时候使用。
简单的总结下几点区别:
- vmalloc分配的地址一般为高地址,kmalloc则从低地址开始分配
- vmalloc分配大内存,kmalloc分配小内存
- vmalloc分配的空间对应的物理地址不连续,kmalloc分配的空间对应的物理地址连续
- kmalloc常用于设备DMA操作,vmalloc不能用于DMA
在操作系统中,/proc/meminfo
文件中提供了kmalloc和vmalloc的分配情况,相关信息如下:
1 | cat /proc/meminfo |grep -E 'Slab|SReclaimable|SUnreclaim|Vmalloc' |
上面输出中,Slab为kmalloc申请的内存信息,其中包括SReclaimable可回收的部分和SUnreclaim不可回收的部分。VmallocUsed为vmalloc申请的内存信息。
观察内核内存分配
上节描述了可以通过系统提供的meminfo文件来观察内核中内存的分配情况,下面通过编写简单的内核模块来进行vmalloc内存分配和释放。
内核模块核心代码:
1 | //vmalloc 分配 |
编写内核模块:
1 |
|
使用makefile进行内核模块编译:
1 | obj-m = kmem_test.o |
编译成内核模块:
看下加载模块后的内存情况:
卸载模块后的内存情况:
上面的测试能便于你更好的理解meminfo文件中的统计信息。那么如果内核发生内存泄漏会怎么样呢?
内存发生内存泄漏,即未释放申请过的内存空间。由于内核空间内存的生命周期和内核生命周期是一致的,所以当发生内核模块导致的内存泄漏时,单单卸载有问题的模块是不能解决问题的,必须通过重启操作系统才能够释放掉这些空间。
可见,内核内存泄漏检查是一个很重要的事,尽量避免第三方模块和设备驱动的内存泄漏。
内核内存泄漏的分析工具-kmemleak
如果我们想要对内核内存泄漏做些基础的分析,最好借助一些内核内存泄漏分析工具,其中最常用的分析工具就是kmemleak。
kmemleak 是内核内存泄漏检查的利器,但是,它的使用也存在一些不便性,因为打开该特性会给性能带来一些损耗,所以生产环境中的内核都会默认关闭该特性。该特性我们一般只用在测试环境中,然后在测试环境中运行需要分析的驱动程序以及其他内核模块。
与其他内存泄漏检查工具类似,kmemleak 也是通过检查内核内存的申请和释放,来判断是否存在申请的内存不再使用也不释放的情况。如果存在,就认为是内核内存泄漏,然后把这些泄漏的信息通过 /sys/kernel/debug/kmemleak 这个文件导出给用户分析。
读者可以自行研究。
使用systemtap动态追踪内存分配
systemtap在之前的文章中有介绍到,具体使用也可以参考使用Systemtap监视TCP连接队列溢出这篇文章。
使用systemtap分析kernel或模块的内存分配和释放,编写stp脚本如下:
1 | // kmem_test.stp |
运行stap后进行模块加载,观察打印出来的信息:
可以看到运行stp脚本后能够追踪到内存分配。但是卸载模块没看到free,不知道是哪里的问题?
疑问:测试了几次还是只有malloc,没抓到free,如果知道原因的作者可以给我留言或者邮件,多谢!
赞赏支持一下