note
内存寻址:虚拟地址映射到物理地址
x86虚拟地址到物理地址机制:分段+分页
linux为了增加通用性,没有分段机制,64位系统采用四级分页机制,也就是有四级页表
寻址过程:虚拟地址->第一级页目录->第二级页目录->第三级页目录->第四级页目录->页表项->物理地址
测试
v2p.c
/* 申请一个页面,然后按照虚拟地址到物理地址的寻页步骤,找到物理地址 */
/* 代码模拟mmu寻页过程 */
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mm.h>
#include <linux/mm_types.h>
#include <linux/sched.h>
#include <linux/export.h>
static unsigned long cr0,cr3;
static unsigned long vaddr = 0; /*虚拟地址*/
static void get_pgtalbe_macro(void) {
cr0 = read_cr0();
cr3 = read_cr3_pa();
printk("cr0:0x%lx,cr3:0x%lx\n", cr0, cr3);
printk("PGDIR_SHIFT:%d\n", PGDIR_SHIFT);
printk("P4D_SHIFT:%d\n", P4D_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_P4D:%d\n", PTRS_PER_P4D);
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;
p4d_t* p4d;
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); /*所有内核进程共享页表,所以用当前进程current的页表*/
printk("pgd_val:0x%lx, pgd_index:%lu\n", pgd_val(*pgd), pgd_index(vaddr));
if (pgd_none(*pgd)) {
printk("not mapping in pgd\n");
return(-1);
}
p4d = p4d_offset(pgd, vaddr);
printk("p4d_val:0x%lx, p4d_index:%lu\n", p4d_val(*p4d), p4d_index(vaddr));
if (p4d_none(*p4d)) {
printk("not mapping in p4d\n");
return(-1);
}
pud = pud_offset(p4d, vaddr);
printk("pud_val:0x%lx, pud_index:%lu\n", pud_val(*pud), pud_index(vaddr));
if (pud_none(*pud)) {
printk("not mapping in pud\n");
return(-1);
}
pmd = pmd_offset(pud, vaddr);
printk("pmd_val:0x%lx, pmd_index:%lu\n", pmd_val(*pmd), pmd_index(vaddr));
if (pmd_none(*pmd)) {
printk("not mapping in pmd\n");
return(-1);
}
pte = pte_offset_kernel(pmd, vaddr);
printk("pte_val:0x%lx, pte_index:%lu\n", pte_val(*pte), pte_index(vaddr));
if (pte_none(*pte)) {
printk("not mapping 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("v2p_init\n");
get_pgtalbe_macro();
vaddr = __get_free_page(GFP_KERNEL); /* 获取空闲内存页 */
if (vaddr == 0) {
printk("__get_free_page error\n");
return(0);
}
printk("__get_free_page return:%lx\n", vaddr);
sprintf((char*)vaddr, "this is v2p_module"); // 往虚拟地址写入内容,将在物理地址读取到
vaddr2paddr(vaddr);
return(0);
}
static void __exit v2p_exit(void) {
printk("v2p_exit\n");
free_page(vaddr);
}
module_init(v2p_init);
module_exit(v2p_exit);
MODULE_LICENSE("GPL");
效果