如何从 Linux 内核模块中的逻辑地址获取物理地址?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6252063/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-05 04:22:13  来源:igfitidea点击:

How to get the physical address from the logical one in a Linux kernel module?

linuxmemory-managementlinux-kernel

提问by Ilya Matveychikov

Is there any suitable way to get the physical address by the logical one except to walk through page directory entries by hand? I've looked for this functionality in kernel's sources and found that there is a follow_pagefunction that do it well with built-in huge and transparent-huge pages support. But it's not exported to kernel modules (why???)...

除了手动遍历页面目录条目之外,是否有任何合适的方法可以通过逻辑地址获取物理地址?我在内核的源代码中寻找了这个功能,发现有一个follow_page函数可以很好地使用内置的巨大和透明的大页面支持。但它没有导出到内核模块(为什么???)...

So, I don't want to invent the wheel and I think that it's not very good to reimplement the follow_pagefunctionality by hand.

所以,我不想发明轮子,我认为follow_page手动重新实现功能不是很好。

采纳答案by Ilya Matveychikov

Well, it might looks as something like that (follow PTE from an virtual address):

好吧,它可能看起来像这样(从虚拟地址跟随 PTE):

void follow_pte(struct mm_struct * mm, unsigned long address, pte_t * entry)
{
    pgd_t * pgd = pgd_offset(mm, address);

    printk("follow_pte() for %lx\n", address);

    entry->pte = 0;
    if (!pgd_none(*pgd) && !pgd_bad(*pgd)) {
        pud_t * pud = pud_offset(pgd, address);
        struct vm_area_struct * vma = find_vma(mm, address);

        printk(" pgd = %lx\n", pgd_val(*pgd));

        if (pud_none(*pud)) {
            printk("  pud = empty\n");
            return;
        }
        if (pud_huge(*pud) && vma->vm_flags & VM_HUGETLB) {
            entry->pte = pud_val(*pud);
            printk("  pud = huge\n");
            return;
        }

        if (!pud_bad(*pud)) {
            pmd_t * pmd = pmd_offset(pud, address);

            printk("  pud = %lx\n", pud_val(*pud));

            if (pmd_none(*pmd)) {
                printk("   pmd = empty\n");
                return;
            }
            if (pmd_huge(*pmd) && vma->vm_flags & VM_HUGETLB) {
                entry->pte = pmd_val(*pmd);
                printk("   pmd = huge\n");
                return;
            }
            if (pmd_trans_huge(*pmd)) {
                entry->pte = pmd_val(*pmd);
                printk("   pmd = trans_huge\n");
                return;
            }
            if (!pmd_bad(*pmd)) {
                pte_t * pte = pte_offset_map(pmd, address);

                printk("   pmd = %lx\n", pmd_val(*pmd));

                if (!pte_none(*pte)) {
                    entry->pte = pte_val(*pte);
                    printk("    pte = %lx\n", pte_val(*pte));
                } else {
                    printk("    pte = empty\n");
                }
                pte_unmap(pte);
            }
        }
    }
}

回答by Gabe

It sounds like you're looking for virt_to_phys.

听起来您正在寻找virt_to_phys.

回答by Pavan Manjunath

I think you can achieve virtual->physical translation through an indirect method by a combination of /proc/[pid]/maps( gives the virtual mapping for a process ) and /proc/[pid]/pagemap( Gives Virtual Page to Physical Page mapping for every addressable page ). First, find out the mapping of virtual addresses of your process from maps( This is done so that you don't search every byte in pagemap) Then check for the physical mapping of the desired virtual address in pagemap ( pagemap is not in text format. Here is a detailed explantion of the format Pagemap) This should give you the exact virtual-->physical mapping

我认为您可以通过/proc/[pid]/maps(为进程提供虚拟映射)和/proc/[pid]/pagemap(为每个可寻址页面提供虚拟页面到物理页面映射)的组合,通过间接方法实现虚拟->物理转换。首先,找出你的进程的虚拟地址的映射maps(这样做是为了你不会搜索中的每个字节pagemap)然后检查页面映射中所需虚拟地址的物理映射(页面映射不是文本格式。这里是对Pagemap格式的详细解释)这应该为您提供确切的虚拟->物理映射