私は組み込みLinuxシステム(kernel-5.10.24)を開発しており、kmemleakの仕組みを理解しようとしています。
文書によると、kmemleakは、参照されていないメモリがあるかどうかを確認するためにデータセグメントをスキャンします。カーネルコードは次のとおりです。
kmemleak_scan()
/*
* Struct page scanning for each node.
*/
get_online_mems();
for_each_populated_zone(zone) {
unsigned long start_pfn = zone->zone_start_pfn;
unsigned long end_pfn = zone_end_pfn(zone);
unsigned long pfn;
for (pfn = start_pfn; pfn < end_pfn; pfn++) {
struct page *page = pfn_to_online_page(pfn);
if (!page)
continue;
/* only scan pages belonging to this zone */
if (page_zone(page) != zone)
continue;
/* only scan if page is in use */
if (page_count(page) == 0)
continue;
scan_block(page, page + 1, NULL);
if (!(pfn & 63))
cond_resched();
}
}
put_online_mems();
各PFNへのポインタを取得しstruct page
てに渡しますscan_block
。
しかもscan_block()
/*
* Scan a memory block (exclusive range) for valid pointers and add those
* found to the gray list.
*/
static void scan_block(void *_start, void *_end,
struct kmemleak_object *scanned)
{
unsigned long *ptr;
unsigned long *start = PTR_ALIGN(_start, BYTES_PER_POINTER);
unsigned long *end = _end - (BYTES_PER_POINTER - 1);
unsigned long flags;
unsigned long untagged_ptr;
raw_spin_lock_irqsave(&kmemleak_lock, flags);
for (ptr = start; ptr < end; ptr++) {
struct kmemleak_object *object;
unsigned long pointer;
unsigned long excess_ref;
if (scan_should_stop())
break;
kasan_disable_current();
pointer = *ptr; ///// ?????
kasan_enable_current();
untagged_ptr = (unsigned long)kasan_reset_tag((void *)pointer);
if (untagged_ptr < min_addr || untagged_ptr >= max_addr)
continue;
/*
* No need for get_object() here since we hold kmemleak_lock.
* object->use_count cannot be dropped to 0 while the object
* is still present in object_tree_root and object_list
* (with updates protected by kmemleak_lock).
*/
object = lookup_object(pointer, 1);
指すポインタstruct page
をに変換しunsigned long *
、逆参照して確認するメモリアドレスをunsigned long *
取得します。pointer
私の混乱は逆参照struct page
PFNを記述する構造へのポインタです。逆参照が構造ページの代わりにメモリアドレスを取得できるのはなぜですか?
私のシステムではサイズstruct page
は32バイトなので、代わりにpage_sizepage+1
だけpage+0x20
を増やします(0x1000)。
答え1
構造体ページへのポインタはメモリアドレスです。例えば。 0x00a0000。page+1
0x00a0020 などの別のアドレスです。
次に、この関数は、0x00a000から0x00a0020までのすべての「潜在的な」メモリポインタを取得します。struct page
ポインタが特定の方法でソートされていると仮定する以外は、フォーマットについて何も知りません。