Linux使用一种4级页表机制,即页全局目录(Page Global Directory)、页上级目录(Page Upper Directory)、页中间目录(Page Middle Directory)和页表(Page Table)。在这种分页机制下.
一个完整的线性地址被分为五部分:页全局目录、页上级目录、页中间目录、页表和偏移量,但是对于每个部分所占的位数则是不定的,这跟系统所在的体系架构有关。
Linux分别采用pgd_t、pud_t 、pmd_t和pte_t四种数据结构来表示页全局目录项、页上级目录项、页中间目录项和页表项。这四种数据结构本质上都是无符号长整型,Linux为了更严格数据类型检查,将无符号长整型分别封装成四种不同的页表项。
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; }
通过代码我们发现线性地址寻值过程:pgd_t -> pud_t ->pmd_t -> pte_t
具体Dmesg打印信息:
dmesg.log
目前在X86_64环境下,虽然支持64位,但是系统实际上目前远远使用不了如此大的内存,真正使用的只有48位,即
0xffff000000000000
然后使用上面的宏函数进行移位操作,拼接出下一级的页表物理基地址。然后再和后面的pud,pmd,pte部分地址相加,得到下一级的物理基地址。直到最后拿到页框的物理地址与pte或,找到真实的物理地址。
参考:
arch/x86/include/asm/pgtable_64_types.h