Linux 中是否有任何 API 可以根据虚拟地址确定物理地址?

2024-10-11 08:36:00
admin
原创
259
摘要:问题描述:Linux 操作系统中是否有任何 API 可以根据虚拟地址确定物理地址?解决方案 1:内核和用户空间使用虚拟地址(也称为线性地址),这些虚拟地址由内存管理硬件映射到物理地址。此映射由操作系统设置的页表定义。DMA 设备使用总线地址。在 i386 PC 上,总线地址与物理地址相同,但其他架构可能有特殊...

问题描述:

Linux 操作系统中是否有任何 API 可以根据虚拟地址确定物理地址?


解决方案 1:

内核和用户空间使用虚拟地址(也称为线性地址),这些虚拟地址由内存管理硬件映射到物理地址。此映射由操作系统设置的页表定义。

DMA 设备使用总线地址。在 i386 PC 上,总线地址与物理地址相同,但其他架构可能有特殊的地址映射硬件来将总线地址转换为物理地址。

在 Linux 中,您可以使用以下函数asm/io.h

  • virt_to_phys(virt_addr);

  • phys_to_virt(phys_addr);

  • virt_to_bus(virt_addr);

  • bus_to_virt(总线地址);

所有这些都是关于访问普通内存的。PCI 或 ISA 总线上还有“共享内存”。可以使用 ioremap() 将其映射到 32 位地址空间内,然后通过 readb()、writeb()(等)函数使用它。

生活因周围存在各种缓存而变得复杂,因此访问相同物理地址的不同方式不一定会产生相同的结果。

此外,虚拟地址背后的实际物理地址可能会发生变化。甚至,在访问该内存之前,可能没有与虚拟地址关联的地址。

至于用户空间 API,据我所知没有。

解决方案 2:

/proc/<pid>/pagemap用户空间最小可运行示例

virt_to_phys_用户.c

#define _XOPEN_SOURCE 700
#include <fcntl.h> /* open */
#include <stdint.h> /* uint64_t  */
#include <stdio.h> /* printf */
#include <stdlib.h> /* size_t */
#include <unistd.h> /* pread, sysconf */

typedef struct {
    uint64_t pfn : 55;
    unsigned int soft_dirty : 1;
    unsigned int file_page : 1;
    unsigned int swapped : 1;
    unsigned int present : 1;
} PagemapEntry;

/* Parse the pagemap entry for the given virtual address.
 *
 * @param[out] entry      the parsed entry
 * @param[in]  pagemap_fd file descriptor to an open /proc/pid/pagemap file
 * @param[in]  vaddr      virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int pagemap_get_entry(PagemapEntry *entry, int pagemap_fd, uintptr_t vaddr)
{
    size_t nread;
    ssize_t ret;
    uint64_t data;
    uintptr_t vpn;

    vpn = vaddr / sysconf(_SC_PAGE_SIZE);
    nread = 0;
    while (nread < sizeof(data)) {
        ret = pread(pagemap_fd, ((uint8_t*)&data) + nread, sizeof(data) - nread,
                vpn * sizeof(data) + nread);
        nread += ret;
        if (ret <= 0) {
            return 1;
        }
    }
    entry->pfn = data & (((uint64_t)1 << 55) - 1);
    entry->soft_dirty = (data >> 55) & 1;
    entry->file_page = (data >> 61) & 1;
    entry->swapped = (data >> 62) & 1;
    entry->present = (data >> 63) & 1;
    return 0;
}

/* Convert the given virtual address to physical using /proc/PID/pagemap.
 *
 * @param[out] paddr physical address
 * @param[in]  pid   process to convert for
 * @param[in] vaddr virtual address to get entry for
 * @return 0 for success, 1 for failure
 */
int virt_to_phys_user(uintptr_t *paddr, pid_t pid, uintptr_t vaddr)
{
    char pagemap_file[BUFSIZ];
    int pagemap_fd;

    snprintf(pagemap_file, sizeof(pagemap_file), "/proc/%ju/pagemap", (uintmax_t)pid);
    pagemap_fd = open(pagemap_file, O_RDONLY);
    if (pagemap_fd < 0) {
        return 1;
    }
    PagemapEntry entry;
    if (pagemap_get_entry(&entry, pagemap_fd, vaddr)) {
        return 1;
    }
    close(pagemap_fd);
    *paddr = (entry.pfn * sysconf(_SC_PAGE_SIZE)) + (vaddr % sysconf(_SC_PAGE_SIZE));
    return 0;
}

int main(int argc, char **argv)
{
    pid_t pid;
    uintptr_t vaddr, paddr = 0;

    if (argc < 3) {
        printf("Usage: %s pid vaddr
", argv[0]);
        return EXIT_FAILURE;
    }
    pid = strtoull(argv[1], NULL, 0);
    vaddr = strtoull(argv[2], NULL, 0);
    if (virt_to_phys_user(&paddr, pid, vaddr)) {
        fprintf(stderr, "error: virt_to_phys_user
");
        return EXIT_FAILURE;
    };
    printf("0x%jx
", (uintmax_t)paddr);
    return EXIT_SUCCESS;
}

GitHub 上游。

用法:

sudo ./virt_to_phys_user.out <pid> <virtual-address>

sudo`/proc/<pid>/pagemap`即使您具有文件权限,也需要阅读,如以下说明: https ://unix.stackexchange.com/questions/345915/how-to-change-permission-of-proc-self-pagemap-file/383838#383838

如在https://stackoverflow.com/a/46247716/895245中所述,Linux 会延迟分配页表,因此请确保在使用之前从测试程序中读取并向该地址写入一个字节virt_to_phys_user

如何测试

测试程序:

#define _XOPEN_SOURCE 700
#include &lt;stdint.h>
#include &lt;stdio.h>
#include &lt;stdlib.h>
#include &lt;unistd.h>

enum { I0 = 0x12345678 };

static volatile uint32_t i = I0;

int main(void) {
    printf(&quot;vaddr %p
&quot;, (void *)&amp;i);
    printf(&quot;pid %ju
&quot;, (uintmax_t)getpid());
    while (i == I0) {
        sleep(1);
    }
    printf(&quot;i %jx
&quot;, (uintmax_t)i);
    return EXIT_SUCCESS;
}

测试程序输出其拥有的变量的地址及其PID,例如:

vaddr 0x600800
pid 110

然后您可以通过以下方式转换虚拟地址:

sudo ./virt_to_phys_user.out 110 0x600800

最后,可以使用观察/修改内存来测试转换/dev/mem,但是如果不重新编译内核,则无法在 Ubuntu 17.04 上执行此操作,因为它需要:CONFIG_STRICT_DEVMEM=n,另请参阅:如何在 Linux 中从用户空间访问物理地址?然而, Buildroot是一种克服该问题的简单方法。

或者,您可以使用类似 QEMU 监视器的虚拟机xp命令:如何在 Linux 中解码 /proc/pid/pagemap 条目?

请参阅此内容以转储所有页面:如何在 Linux 中解码 /proc/pid/pagemap 条目?

该问题的用户空间子集:如何在 Linux 中从用户空间找到变量的物理地址?

使用以下命令转储所有进程页面/proc/&lt;pid>/maps

/proc/&lt;pid>/maps列出进程的所有地址范围,因此我们可以沿着它来翻译所有页面:/proc/[pid]/pagemaps 和 /proc/[pid]/maps | linux

Kernelandvirt_to_phys()仅适用于kmalloc()地址

从内核模块中,virt_to_phys()已经提到了。

然而,必须强调的是,它有这个局限性。

例如,模块变量失败。arc/x86/include/asm/io.h文档:

返回的物理地址是给定内存地址的物理(CPU)映射。仅对直接映射或通过分配的地址使用此函数才有效kmalloc()

这是一个内核模块,与用户空间测试一起说明了这一点。

所以这不是一种很普遍的可能性。请参阅:如何在 Linux 内核模块中从逻辑地址获取物理地址?仅了解内核模块方法。

解决方案 3:

正如之前所回答的,普通程序不需要担心物理地址,因为它们在虚拟地址空间中运行,并具有所有便利性。此外,并非每个虚拟地址都有物理地址,它们可能属于映射文件或交换页面。但是,有时即使在用户空间中看到这种映射也很有趣。

为此,Linux 内核通过 中的一组文件将其映射暴露给用户空间/proc。文档可在此处找到。简要摘要:

  1. /proc/$pid/maps提供虚拟地址映射列表以及其他信息,例如映射文件的对应文件。

  2. /proc/$pid/pagemap提供有关每个映射页面的更多信息,包括物理地址(如果存在)。

该网站提供了一个 C 程序,该程序转储了使用此接口的所有正在运行的进程的映射,并解释了它的作用。

解决方案 4:

上面建议的 C 程序通常可以工作,但它可能会以(至少)两种方式返回误导性的结果:

  1. 页面不存在(但虚拟地址已映射到页面!)。这是由于操作系统的延迟映射而发生的:它仅在实际访问地址时才映射地址。

  2. 返回的 PFN 指向某个可能临时的物理页面,由于写时复制,该页面可能很快会发生变化。例如:对于内存映射文件,PFN 可以指向只读副本。对于匿名映射,映射中所有页面的 PFN 可能是一个特定的只读页面,其中全是 0(写入时所有匿名页面都会从该页面生成)。

底线是,为了确保结果更可靠:对于只读映射,在查询其 PFN 之前至少读取每个页面一次。对于可写入页面,在查询其 PFN 之前至少写入每个页面一次。

当然,从理论上讲,即使获得了“稳定”的 PFN,映射在运行时也总是可以任意改变(例如,将页面移入和移出交换时),因此不应依赖它。

解决方案 5:

我想知道为什么没有用户空间 API。

因为用户空间内存的物理地址是未知的。

Linux 对用户空间内存使用按需分页。您的用户空间对象在被访问之前不会拥有物理内存。当系统内存不足时,您的用户空间对象可能会被换出并失去物理内存,除非页面被锁定用于进程。当您再次访问该对象时,它会被换入并被赋予物理内存,但它可能与前一个物理内存不同。您可以对页面映射进行快照,但不能保证下一刻的快照是一样的。

因此,寻找用户土地对象的物理地址通常是没有意义的。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   2329  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1502  
  在当今快速变化的商业环境中,项目管理工具已成为企业提升效率、优化资源分配和确保项目成功的关键。无论是初创公司还是大型企业,选择合适的项目管理软件都能显著提高团队协作和项目交付的质量。本文将为您推荐2025年不同规模企业的10个专业项目管理工具,帮助您找到最适合的解决方案。信创国产项目管理软件 - 禅道禅道是一款国产开源...
项目管理平台   1  
  工程项目管理涉及众多环节与复杂流程,从项目规划、资源分配到进度跟踪、成本控制等,每一步都对项目的成功交付起着关键作用。在数字化时代,借助专业的工程项目管理软件能极大提升管理效率与质量。然而,面对市场上琳琅满目的软件产品,如何选择适合的软件并熟练运用,成为众多工程项目管理者亟待解决的问题。明确自身需求在选择工程项目管理软...
项目风险管理   1  
  PLM(产品生命周期管理)项目管理旨在确保产品从概念到退役的整个生命周期内的有效规划、执行和控制。然而,在项目推进过程中,黑天鹅事件的出现往往会打乱原有的计划和节奏。黑天鹅事件具有意外性、重大影响和事后可解释性的特点,给PLM项目管理带来巨大挑战。如何有效应对这些难以预测的事件,制定合理的应急计划,成为项目管理者必须面...
plm研发管理系统   20  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用