Linux内核-如何获取物理地址(内存管理)? [英] linux kernel - how to get physical address (memory management)?

查看:567
本文介绍了Linux内核-如何获取物理地址(内存管理)?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在linux中,

页面全局目录偏移地址(cr3 +索引)可以使用 pgd_offset()MACRO计算出来.

Page Global Directory offset address(cr3 + index) can be calculated using pgd_offset() MACRO.

页面上层目录偏移地址可以使用 pud_offset()API计算.

Page Upper Directory offset address can be calculated using pud_offset() API.

页面中间目录偏移地址可以使用 pmd_offset()API计算.

Page Middle Directory offset address can be calculated using pmd_offset() API.

页面表条目的偏移地址可以使用 pte_offset_map()MACRO计算.

Page Table Entry offset address can be calculated using pte_offset_map() MACRO.

然后,如何获取物理地址? (yellow line in above picture)

Then, how to get physical address? (yellow line in above picture)

是否存在用于计算物理地址的函数或MACRO?

Is there a function or MACRO to calculate physical address?

edit : x86-64 architecture.

推荐答案

Linux内核使用通用的四页分页模型,该模型不仅适用于32位系统,而且还适用于64位系统.分页单元是MMU(内存管理单元)的一部分,MMU将线性地址转换为物理地址.

The Linux kernel uses a generic four-page paging model, which is not only suitable for 32-bit systems but also for 64-bit systems. The paging unit is part of the MMU (Memory Management Unit), which converts a linear address into a physical address.

我为您编写了一个内核模块,以模拟从虚拟地址到物理地址的转换过程.我假设您知道分页系统的原理.

I wrote a kernel module for you to simulate the process of virtual address conversion to physical address. I am assuming you know the principal of paging system.

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);
}

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

  1. Get_pgtable_macro () Prints some macros in the current system paging mechanism.

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

Through vmalloc () in the allocation of memory space in kernel space, calling vaddr2paddr () will be converted into a virtual address physical address.

Vaddr2paddr()执行如下:

Vaddr2paddr () is executed as follows:

  1. 通过pgd_offset计算页面全局编录条目的线性地址pgd,并传入内存描述符mm和线性地址vaddr.接下来,打印pgd指向的页面全局编录条目.

  1. Calculate the linear address pgd of the page global catalog entry by pgd_offset, passing in the memory descriptor mm and the linear address vaddr. Next, print the page global catalog entry pointed to by pgd.

通过pud_offset计算页面父目录条目的线性地址pud,并将参数传递给页面全局目录条目的线性地址pgd和线性地址vaddr.然后打印指向父目录条目的pud.

Calculate the linear address pud of the page parent directory entry by pud_offset, passing the parameters to the linear address pgd of the page global directory entry and the linear address vaddr. Then print the pud referred to the parent directory entry.

通过pmd_offset计算页面中间目录条目的线性地址pmd,并将参数传递给父目录条目的线性地址pud和线性地址vaddr.然后打印指向pmd目录条目的页面中间.

Calculate the linear address pmd of the page middle directory entry through pmd_offset, passing the parameters to the linear address pud and the linear address vaddr of the parent directory entry. Then print the middle of the page referred to pmd directory entries.

Pte_offset_kernel pte_offset_kernel由线性地址pte计算,该参数为线性地址pmd的线性地址和地址vaddr的目录条目中间.然后打印pte指向的页表项.

Pte_offset_kernel pte_offset_kernel calculated by the linear address pte, the parameters for the middle of the directory entry of the linear address pmd linear address and address vaddr. Then print the page table item pointed to by pte.

pte_val(* pte)删除页面表条目,并且PAGE_MASK阶段和结果是访问页面的物理地址; vaddr& 〜PAGE_MASK用于获取线性地址偏移量字段;这两个或最终的物理地址计算.

pte_val (* pte) to remove the page table entries, and PAGE_MASK phase and the result is to access the page's physical address; vaddr & ~ PAGE_MASK used to get linear address offset field; the two or the final physical address calculation.

打印物理地址

这篇关于Linux内核-如何获取物理地址(内存管理)?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆