在linux中页面全局目录偏移地址(cr3 + index)可以使用pgd_offset()MACRO计算。页面上目录偏移地址可以通过pud_offset()API计算。页面中间目录偏移地址可以通过pmd_offset()API计算。页表条目偏移地址可以使用pte_offset_map()MACRO计算。那么,如何获取物理地址?(yellow line in above picture)是否有函数或宏来计算物理地址?edit : x86-64 architecture.
(yellow line in above picture)
edit : x86-64 architecture.
ncgqoxb01#
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 frame physical address mechanism | offset */ 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); }
rxztt3cl2#
**更新:**五级页表:pud_offset(p4d_t * p4 d,无符号长地址);
p4d_t* p4d; p4d = p4d_offset(pgd, vaddr); if (p4d_none(*p4d) || p4d_bad(*p4d)) { printk("not mapped in p4d\n"); return -1; } pud = pud_offset(p4d, vaddr); printk("pud_val = 0x%lx\n", pud_val(*pud)); if (pud_none(*pud)) { printk("not mapped in pud\n"); return -1; }
https://lwn.net/Articles/717293/Getting error when compiling kernel for page table walk
2条答案
按热度按时间ncgqoxb01#
Linux内核使用通用的四页分页模型,它不仅适用于32位系统,也适用于64位系统。分页单元是MMU(内存管理单元)的一部分,它将线性地址转换为物理地址。
我写了一个内核模块来模拟虚拟地址到物理地址的转换过程。我想你知道寻呼系统的原理。
1.通过vmalloc()在内核空间分配内存空间,调用vaddr 2 paddr()将虚拟地址转换为物理地址。
1.使用vaddr 2 paddr()将虚拟地址转换为物理地址,方法是使用__get_free_pages()在内核空间中分配帧。
1.分别通过vfree()和free_page()释放请求的内存空间。
Vaddr 2 paddr()执行如下:
1.通过pgd_offset计算页全局目录条目的线性地址pgd,传入内存描述符mm和线性地址vaddr。接下来,打印pgd指向的页面全局编录条目。
1.通过pud_offset计算页面父目录条目的线性地址pud,将参数传递给页面全局目录条目的线性地址pgd和线性地址vaddr。然后打印引用到父目录条目的pud。
1.通过pmd_offset计算页面中间目录项的线性地址pmd,将参数传递给父目录项的线性地址pud和线性地址vaddr。然后打印页面中间提到的pmd目录条目。
1.打印物理地址
rxztt3cl2#
**更新:**五级页表:pud_offset(p4d_t * p4 d,无符号长地址);
https://lwn.net/Articles/717293/
Getting error when compiling kernel for page table walk