好记性不如铅笔头

kernel, linux, 操作系统

Linux2.6源码注释笔记:shadow.c

最近在学习Linux源码,感觉啃起来好吃力啊。只能一点点的打持久战了。

CONTENTS

shadow.c:

#include <linux/kmemcheck.h>
#include <linux/module.h>
#include <linux/mm.h>

#include <asm/page.h>
#include <asm/pgtable.h>

#include "pte.h"
#include "shadow.h"

/*
 * Return the shadow address for the given address. Returns NULL if the
 * address is not tracked.
 *
 * We need to be extremely careful not to follow any invalid pointers,
 * because this function can be called for *any* possible address.
 */
/* 查找某块地址对应的shadow地址 */
void *kmemcheck_shadow_lookup(unsigned long address)
{
	pte_t *pte;
	struct page *page;

	if (!virt_addr_valid(address))
		return NULL;

	pte = kmemcheck_pte_lookup(address);
	if (!pte)
		return NULL;

     /* 查找该地址对应的页。 */
	page = virt_to_page(address);
	if (!page->shadow)
		return NULL;
    /* 根据页的shadow地址进行偏移,获取address对应的shadow地址,
     * 这也是为什么标记shadow时需要按照页进行分隔,shadow地址是以页进行管理的
     */
	return page->shadow + (address & (PAGE_SIZE - 1));
}

/*
核心函数,用来标记某块内存的状态。
address:起始地址
n:内存长度
status:要标记的状态
*/
static void mark_shadow(void *address, unsigned int n,
	enum kmemcheck_shadow status)
{
    unsigned long addr = (unsigned long) address; /* 起始地址 */
    unsigned long last_addr = addr + n - 1;/* 末尾地址 */

    /* 根据起始地址和末尾地址获取该块内存所在页  */
    unsigned long page = addr & PAGE_MASK;
	unsigned long last_page = last_addr & PAGE_MASK;

    /*
     * 由于内存可能会跨页边界,因此可能需要多次标记才可以完成该块内存的标记工作。
     * first_n的意思是第一次需要标记的内存的数量。
    */
    unsigned int first_n;

    /* shadow表示该内存地址对应的状态内存的起始地址 */
    void *shadow;

	/* If the memory range crosses a page boundary, stop there. */
	if (page == last_page)
        first_n = n; /* 如果没有跨页,那么一次就可以标记完成,第一次 = 总数量 */
	else
        first_n = page + PAGE_SIZE - addr;/* 跨页了,就得一页一页来标记 */

    /* 根据该块内存的起始地址查找shadow起始地址 */
	shadow = kmemcheck_shadow_lookup(addr);
     /* 更改shadow内存的状态 */
    if (shadow)
		memset(shadow, status, first_n);

    /* 第一次搞定之后,更新变量 */
	addr += first_n;
	n -= first_n;

     /* 如果内存没有跨页,那么上面的memset已经完成了标记,这里就不会进入while,
      * 进入while的都是跨页的。
      * /
	/* Do full-page memset()s. */
	while (n >= PAGE_SIZE) {
         /* 相同操作,根据内存地址查找对应的shadow内存地址,并且更改状态。 */
		shadow = kmemcheck_shadow_lookup(addr);
		if (shadow)
			memset(shadow, status, PAGE_SIZE);

		addr += PAGE_SIZE;
		n -= PAGE_SIZE;
	}

    /* 处理最后剩下的内存 */
	/* Do the remaining page, if any. */
	if (n > 0) {
		shadow = kmemcheck_shadow_lookup(addr);
		if (shadow)
			memset(shadow, status, n);
	}
}

/* 将内存标记为 unallocated,内部调用 mark_shadow */
void kmemcheck_mark_unallocated(void *address, unsigned int n)
{
	mark_shadow(address, n, KMEMCHECK_SHADOW_UNALLOCATED);
}

/* 将内存标记为 uninitialized,内部调用 mark_shadow */
void kmemcheck_mark_uninitialized(void *address, unsigned int n)
{
	mark_shadow(address, n, KMEMCHECK_SHADOW_UNINITIALIZED);
}

/*
 * Fill the shadow memory of the given address such that the memory at that
 * address is marked as being initialized.
 */
/* 将内存标记为 initialized,内部调用 mark_shadow */
void kmemcheck_mark_initialized(void *address, unsigned int n)
{
	mark_shadow(address, n, KMEMCHECK_SHADOW_INITIALIZED);
}
EXPORT_SYMBOL_GPL(kmemcheck_mark_initialized);

/* 将内存标记为 freed,内部调用 mark_shadow */
void kmemcheck_mark_freed(void *address, unsigned int n)
{
	mark_shadow(address, n, KMEMCHECK_SHADOW_FREED);
}
/* 将指定的整页内存标记为 unallocated,内部调用 mark_shadow */
void kmemcheck_mark_unallocated_pages(struct page *p, unsigned int n)
{
	unsigned int i;

	for (i = 0; i < n; ++i)
		kmemcheck_mark_unallocated(page_address(&p[i]), PAGE_SIZE);
}
/* 将指定的整页内存标记为 uninitialized,内部调用 mark_shadow */
void kmemcheck_mark_uninitialized_pages(struct page *p, unsigned int n)
{
	unsigned int i;

	for (i = 0; i < n; ++i)
		kmemcheck_mark_uninitialized(page_address(&p[i]), PAGE_SIZE);
}

/* 将指定的整页内存标记为 initialized,内部调用 mark_shadow */
void kmemcheck_mark_initialized_pages(struct page *p, unsigned int n)
{
	unsigned int i;

	for (i = 0; i < n; ++i)
		kmemcheck_mark_initialized(page_address(&p[i]), PAGE_SIZE);
}

enum kmemcheck_shadow kmemcheck_shadow_test(void *shadow, unsigned int size)
{
	uint8_t *x;
	unsigned int i;

	x = shadow;

#ifdef CONFIG_KMEMCHECK_PARTIAL_OK
	/*
	 * Make sure _some_ bytes are initialized. Gcc frequently generates
	 * code to access neighboring bytes.
	 */
	for (i = 0; i < size; ++i) {
		if (x[i] == KMEMCHECK_SHADOW_INITIALIZED)
			return x[i];
	}
#else
	/* All bytes must be initialized. */
	for (i = 0; i < size; ++i) {
		if (x[i] != KMEMCHECK_SHADOW_INITIALIZED)
			return x[i];
	}
#endif

	return x[0];
}

void kmemcheck_shadow_set(void *shadow, unsigned int size)
{
	uint8_t *x;
	unsigned int i;

	x = shadow;
	for (i = 0; i < size; ++i)
		x[i] = KMEMCHECK_SHADOW_INITIALIZED;
}

关于跨页和不跨页:

不跨页时:

跨页时:

发表评论

2 × 2 =

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据