轉自: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);
- 問題