日志标签 ‘内核应用’

Linux内存管理实践-虚拟地址转换物理地址

2011年11月1日

Linux内核中采用了通用的四级分页模型,这种模型不仅适合32位系统也适合64位系统。分页单元是MMU(内存管理单元)中的一部分,它将线性地址转换为物理地址。本文通过一个内核模块程序模拟内核中虚拟地址转换为物理地址的过程,有关分页机制的原理可以参见这里的文章。

static void get_pgtable_macro(void)
{
	printk("PAGE_OFFSET = 0x%lx\n", PAGE_OFFSET);
	printk("PGDIR_SHIFT = %d\n", PGDIR_SHIFT);
	printk("PUD_SHIFT = %d\n", PUD_SHIFT);
	printk("PMD_SHIFT = %d\n", PMD_SHIFT);
	printk("PAGE_SHIFT = %d\n", PAGE_SHIFT);

	printk("PTRS_PER_PGD = %d\n", PTRS_PER_PGD);
	printk("PTRS_PER_PUD = %d\n", PTRS_PER_PUD);
	printk("PTRS_PER_PMD = %d\n", PTRS_PER_PMD);
	printk("PTRS_PER_PTE = %d\n", PTRS_PER_PTE);

	printk("PAGE_MASK = 0x%lx\n", PAGE_MASK);
}

static unsigned long vaddr2paddr(unsigned long vaddr)
{
	pgd_t *pgd;
	pud_t *pud;
	pmd_t *pmd;
	pte_t *pte;
	unsigned long paddr = 0;
        unsigned long page_addr = 0;
	unsigned long page_offset = 0;

	pgd = pgd_offset(current->mm, vaddr);
	printk("pgd_val = 0x%lx\n", pgd_val(*pgd));
	printk("pgd_index = %lu\n", pgd_index(vaddr));
	if (pgd_none(*pgd)) {
		printk("not mapped in pgd\n");
		return -1;
	}

	pud = pud_offset(pgd, vaddr);
	printk("pud_val = 0x%lx\n", pud_val(*pud));
	if (pud_none(*pud)) {
		printk("not mapped in pud\n");
		return -1;
	}

	pmd = pmd_offset(pud, vaddr);
	printk("pmd_val = 0x%lx\n", pmd_val(*pmd));
	printk("pmd_index = %lu\n", pmd_index(vaddr));
	if (pmd_none(*pmd)) {
		printk("not mapped in pmd\n");
		return -1;
	}

	pte = pte_offset_kernel(pmd, vaddr);
	printk("pte_val = 0x%lx\n", pte_val(*pte));
	printk("pte_index = %lu\n", pte_index(vaddr));
	if (pte_none(*pte)) {
		printk("not mapped in pte\n");
		return -1;
	}

	//页框物理地址机制 | 偏移量
	page_addr = pte_val(*pte) & PAGE_MASK;
	page_offset = vaddr & ~PAGE_MASK;
	paddr = page_addr | page_offset;
	printk("page_addr = %lx, page_offset = %lx\n", page_addr, page_offset);
        printk("vaddr = %lx, paddr = %lx\n", vaddr, paddr);

	return paddr;
}

static int __init v2p_init(void)
{
	unsigned long vaddr = 0;

	printk("vaddr to paddr module is running..\n");
	get_pgtable_macro();
	printk("\n");

	vaddr = (unsigned long)vmalloc(1000 * sizeof(char));
	if (vaddr == 0) {
		printk("vmalloc failed..\n");
		return 0;
	}
	printk("vmalloc_vaddr=0x%lx\n", vaddr);
	vaddr2paddr(vaddr);

	printk("\n\n");
	vaddr = __get_free_page(GFP_KERNEL);
	if (vaddr == 0) {
		printk("__get_free_page failed..\n");
		return 0;
	}
	printk("get_page_vaddr=0x%lx\n", vaddr);
	vaddr2paddr(vaddr);

	return 0;
}

static void __exit v2p_exit(void)
{
	printk("vaddr to paddr module is leaving..\n");
        vfree((void *)vaddr);
        free_page(vaddr);
}

整个程序的结构如下:

1.get_pgtable_macro()打印当前系统分页机制中的一些宏。

2.通过vmalloc()在内核空间中分配内存,调用vaddr2paddr()将虚拟地址转化成物理地址。

3.通过__get_free_pages()在内核空间中分配页框,调用vaddr2paddr()将虚拟地址转化成物理地址。

4.分别通过vfree()和free_page()释放申请的内存空间。

vaddr2paddr()的执行过程如下:

1.通过pgd_offset计算页全局目录项的线性地址pgd,传入的参数为内存描述符mm和线性地址vaddr。接着打印pgd所指的页全局目录项。

2.通过pud_offset计算页上级目录项的线性地址pud,传入的参数为页全局目录项的线性地址pgd和线性地址vaddr。接着打印pud所指的页上级目录项。

3.通过pmd_offset计算页中间目录项的线性地址pmd,传入的参数为页上级目录项的线性地址pud和线性地址vaddr。接着打印pmd所指的页中间目录项。

4.通过pte_offset_kernel计算页表项的线性地址pte,传入的参数为页中间目录项的线性地址pmd和线性地址vaddr。接着打印pte所指的页表项。

5.pte_val(*pte)先取出页表项,与PAGE_MASK相与的结果是得到要访问页的物理地址;vaddr&~PAGE_MASK用来得到线性地址offset字段;两者或运算得到最终的物理地址。

6.打印物理地址。

list.h的简单应用

2010年8月18日

既然我们分析了list.h,那么就要学以致用,因为通过具体的例子我们才能真实感受到双链表的用法。本文首先为你呈现一个最基本的双链表使用方法,然后在引用另外两个例子,大家可以去亲自试试。

1.简单的学生管理系统

这里学生管理系统只是个基本模型:用户输入数据,然后通过输出重定向到stuInfo.txt文件当中。先看学生信息数据结构:

struct postinfo
{
	char id[20];
	char name[20];
	char sex[10];
	char addr[50];
	char email[20];
	struct list_head list;
};

正如我们前面所说的那样,整个双链表是通过struct list_head结构链接起来的,这样我们每次对某个结点的操作,都是先获得list字段的地址,进而通过list_entry宏获得当前结点的地址。

首先,创建头指针。注意我这里说的是头指针,并不是头结点,因此你应该可以理解为什么下面会首先创建一个struct list_head类型的变量,而不是struct postinfo类型的变量。

	struct list_head head;
	INIT_LIST_HEAD(&head);

创建好头指针,我们接下来就去增加学生信息。每次都先生成一个struct postinfo类型的变量tmp,再将每次要增加的信息都暂时保存在此变量中,接着就利用上次我们所说的增加函数:

list_add_tail(&(tmp->list),head);

注意这里我们使用的是&(tmp->list),正如我们一开始所说的每次对结点的操作实际上都是通过list字段去完成的。

添加完毕后,如何去打印数据?使用我们的遍历宏。pos只是一个struct list_head类型的指针,这个宏会首先使得pos指向head->next,即list链表的第一个结点(而非第一个学生信息结点)。每次移动链表后,通过pos获得当前结点的地址,那么就可以获得其他数据字段的地址了。

	list_for_each(pos,head)
	{
		pinfo=list_entry(pos,struct postinfo,list);
		printf("%s %s %s %s %s\n",pinfo->id,pinfo->name,pinfo->sex,pinfo->addr,pinfo->email);
        }

这个演示程序关键部分就是这样,其他地方只要你有C语言基础,就可以完成的。

2.遍历进程

具体过程请点击这里

windows 7 ultimate product key

windows 7 ultimate product key

winrar download free

winrar download free

winzip registration code

winzip registration code

winzip free download

winzip free download

winzip activation code

winzip activation code

windows 7 key generator

windows 7 key generator

winzip freeware

winzip freeware

winzip free download full version

winzip free download full version

free winrar download

free winrar download

free winrar

free winrar

windows 7 crack

windows 7 crack

windows xp product key

windows xp product key

windows 7 activation crack

windows7 activation crack

free winzip

free winzip

winrar free download

winrar free download

winrar free

winrar free

download winrar free

download winrar free

windows 7 product key

windows 7 product key