商务网站营销推广方式,网站怎么做商桥,如何用html做网站头像,建筑行业征信查询平台官网内存泄露是一个很容易出现的问题#xff0c;尤其是对于测试不太充分的代码。怎么判断出现内存泄露了呢#xff1f;很简单#xff0c;就跑一些简单的测试#xff0c;等待足够长时间即可。内存总有耗尽的时候#xff0c;这时候内核会触发OOM#xff0c;根据oom_score选择一…内存泄露是一个很容易出现的问题尤其是对于测试不太充分的代码。怎么判断出现内存泄露了呢很简单就跑一些简单的测试等待足够长时间即可。内存总有耗尽的时候这时候内核会触发OOM根据oom_score选择一个进程杀掉。这种时候多半是有问题了。
或者在某个进程运行的时候看/proc/meminfo 观察空闲内存部分一直下降多半是有问题了。这就确定有内存泄露了可能是用户态进程泄露也可能是内核泄露。可以有多种方法来分辨
观察meminfo中的slab项如果这一项有异常的增长或者只增不减一路狂飙那八成是内核漏出去了OOM killer会根据oom score选择一个进程杀掉假如选择了desktop Xserver等等就说明找不到分更高的进城了这几个进程nice值是很低的。这都能选上那就是用户态进程没啥占内存特别高的进程了差不多也能确定是内核泄露了。OOM killer杀了进程之后观察OOM打出来的内存信息并没有收回来多少。内核的内存泄露不管OOM选择的是哪个进程来杀内存都收不回来的系统使用起来依然觉得卡卡的那就是内核泄露了。
现在确定了内核泄露的方法接下来怎么找到泄漏点呢下面会介绍几种调试的方法。在此之前先简单实现一个有内存泄漏的内核模块。
#include linux/init.h
#include linux/kernel.h
#include linux/module.h
#include linux/slab.h
#include linux/vmalloc.h
#include linux/list.h
#include linux/percpu.h
#include linux/fdtable.hstruct list_node {long header[25];struct list_head list;char name[25];
};static LIST_HEAD(test_list);/** Some very simple testing. This function needs to be extended for* proper testing.*/
static int __init module_init(void)
{struct list_node *list;int i;pr_info(This is test mode for memleak debug tools!\n);/*alloc */pr_info(kmalloc(32) %p\n, kmalloc(32, GFP_KERNEL));pr_info(kmalloc(32) %p\n, kmalloc(32, GFP_KERNEL));pr_info(kmalloc(1024) %p\n, kmalloc(1024, GFP_KERNEL));pr_info(kmalloc(1024) %p\n, kmalloc(1024, GFP_KERNEL));pr_info(kmalloc(2048) %p\n, kmalloc(2048, GFP_KERNEL));pr_info(kmalloc(2048) %p\n, kmalloc(2048, GFP_KERNEL));pr_info(kmalloc(4096) %p\n, kmalloc(4096, GFP_KERNEL));pr_info(kmalloc(4096) %p\n, kmalloc(4096, GFP_KERNEL));pr_info(vmalloc(64) %p\n, vmalloc(64));pr_info(vmalloc(64) %p\n, vmalloc(64));pr_info(vmalloc(64) %p\n, vmalloc(64));pr_info(vmalloc(64) %p\n, vmalloc(64));pr_info(vmalloc(64) %p\n, vmalloc(64));/** create a list have 10 nodes*/for (i 0; i 10; i) {list kzalloc(sizeof(*list), GFP_KERNEL);pr_info(kzalloc(sizeof(*list)) %p\n, list);if (!list)return -ENOMEM;INIT_LIST_HEAD(list-list);list_add_tail(list-list, test_list);}return 0;
}
module_init(module_init);static void __exit module_exit(void)
{struct test_node *list, *tmp;//delete all node in testlist_for_each_entry_safe(list, tmp, test_list, list)list_del(list-list);
}
module_exit(module_exit);MODULE_LICENSE(GPL);kmemleak
第一个debug的方法也是最简单的——使用Kmemleak这是一种用于检测内核内存泄漏的工具可以用来检测目前内核中可能存在的泄露。kmemleak 是一种运行时的工具它可以在内核运行时进行内存泄漏检测但也会带来一定的性能开销。因此通常情况下发行版并不会打开此工具。
Kmemleak使用
先打开Kmemleak的相关配置
CONFIG_DEBUG_KMEMLEAK //kmemleak总开关
CONFIG_DEBUG_KMEMLEAK_DEFAULT_OFF //是不是默认禁用kmemleak非必须打开选项之后可以通过cmdline kmemleakon 打开kmemleak关闭这个选项也可以通过cmdline kmemleakoff禁用编译安装内核 能看到/sys/kernel/debug/kmemleak这个文件说明kmemleak已经配置好了。 make 并insmod写好的test模块 触发内存scan 模块插入后等几分钟执行echo scan /sys/kernel/debug/kmemleak触发一次内存scan去找可能存在的内存泄露这是整个内存的scan可能需要点时间。 查看结果 通过cat /sys/kernel/debug/kmemleak查看内存泄漏的结果这时候可以下面的结果
# cat /sys/kernel/debug/kmemleak
unreferenced object 0xffff89862ca702e8 (size 32):
comm modprobe, pid 2088, jiffies 4294680594 (age 375.486s)hex dump (first 32 bytes):6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b kkkkkkkkkkkkkkkk6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b 6b a5 kkkkkkkkkkkkkkk.backtrace:[00000000e0a73ec7] 0xffffffffc01d3446[000000000c5d2a46] do_one_initcall0x41/0x1df[0000000046db7e0a] do_init_module0x55/0x200[00000000542b9814] load_module0x203c/0x2480[00000000c2850256] __do_sys_finit_module0xba/0xe0[000000006564e7ef] do_syscall_640x43/0x110[000000007c873fa6] entry_SYSCALL_64_after_hwframe0x44/0xa9calltrace最上面一行就是执行malloc的位置可以根据addr2line或者gdb把地址和代码对应起来。是不是超级简单超级容易。打开选项重编一个内核就知道所有可能泄漏的位置。
为什么说可能存在的泄露位置呢因为Kmemleak可能会存在误报的情况。
kmemleak误报
kmemleak工作流程大概分为三部分标记分配、检测释放和报告泄露。 标记分配的内存kmemleak 会跟踪内核中的动态内存分配包括 kmalloc、kzalloc、vmalloc 等函数分配的内存块。标记这些分配的内存块并记录它们的地址和大小。 检测释放当内存块被释放时kmemleak 会记录释放的内存块并在一段时间后检查是否仍然存在对这些内存块的引用。 报告泄漏扫描内存并报告这些内存泄漏扫描方法如下 标记所有对象为白色初始时kmemleak 会将所有的内存对象标记为白色。在后续的扫描中仍然被标记为白色的对象将被视为孤立对象orphan。 扫描内存从数据段和栈开始扫描内存检查其中的数值是否与存储在红黑树rbtree中的地址相匹配。如果发现指向白色对象的指针则将该对象添加到灰色列表中。 扫描灰色对象对灰色对象进行扫描查找匹配的地址。一些白色对象可能会变为灰色并被添加到灰色列表的末尾直到灰色集合完成扫描。 报告孤立对象剩余的白色对象被视为孤立对象并通过 /sys/kernel/debug/kmemleak 报告。
内核中本身有一些内存是不需要释放的比如说核心相关的部分do_init_call函数或者此块内存地址可以通过其他的什么方法计算得到这样内存也不会有引用技术但是kmemleak会把这块内存当泄露处理。
虽然会误报但是绝大部分情况是非常非常好用的几乎一切内存泄露都逃不过kmemleak的眼睛慧眼如炬
然而接下来来了一个这样的场景在生产服务器上可能出现了内存泄露或者根本拿不到内核代码的os上出现了内存泄露这下怎么办呢Kmemleak是绝对不会在任何一个非debug发行版上开启的这就引入了第二个方式——如何在不修改内核代码的情况下拿到内核运行的相关内容。