如何将虚拟内存地址转换为物理地址?

Posted

技术标签:

【中文标题】如何将虚拟内存地址转换为物理地址?【英文标题】:How to translate a virtual memory address to a physical address? 【发布时间】:2010-09-26 20:47:58 【问题描述】:

在我的 C++ 程序(在 Windows 上)中,我正在分配一块内存,并且可以确保它在物理内存中保持锁定(未交换且连续)(即使用 VirtualAllocEx()、MapUserPhysicalPages() 等)。

在我的进程上下文中,我可以获得该块的虚拟内存地址, 但我需要找出它的物理内存地址 以便将其传递给某个外部设备。

1. 有什么方法可以在我的程序中以 USER 模式将虚拟地址转换为物理地址? 2. 如果没有,我只能在 KERNEL 模式下找到这个虚拟到物理的映射。我想这意味着我必须编写一个驱动程序来做到这一点......?您是否知道我可以使用的任何现成的驱动程序/DLL/API,我的应用程序(程序)将与之交互以进行翻译? 3. 如果我必须自己编写驱动程序,我该如何进行翻译?我使用哪些功能?是 mmGetPhysicalAddress() 吗?我该如何使用它? 4. 另外,如果我理解正确,mmGetPhysicalAddress() 返回调用进程上下文中的虚拟基地址的物理地址。但是,如果调用进程是驱动程序,并且我正在使用我的应用程序来调用该函数的驱动程序,那么我正在更改上下文并且在调用 mmGetPhysicalAddress 例程时我不再处于应用程序的上下文中......所以如何转换应用程序(用户模式)内存空间中的虚拟地址,而不是驱动程序?

任何答案、提示和代码摘录将不胜感激!

谢谢

【问题讨论】:

我很好奇,外部设备可以用物理内存地址做什么? 没有详细说明,它是一个板载设备,可以直接访问机器的 RAM,但不知道操作系统映射(这就是为什么虚拟地址或使用共享内存对象不起作用的原因) . 【参考方案1】:

1) 没有

2) 是的,您必须编写驱动程序。最好是虚拟驱动程序,或者更改特殊外部设备的驱动程序。

3) 这在这里变得非常混乱。 MmGetPhysicalAddress应该是你要找的方法,但是我真的不知道物理地址是怎么映射到bank/chip/etc的。在物理内存上。

4) 你不能使用分页内存,因为它会被重新定位。您可以在 MDL 上使用 MmProbeAndLockPages 锁定分页内存,您可以在从用户模式调用上下文传入的内存上构建。但最好分配非分页内存并将其交给您的用户模式应用程序。

PVOID p = ExAllocatePoolWithTag( NonPagedPool, POOL_TAG );
PHYSICAL_ADDRESS realAddr = MmGetPhysicalAddress( p );

// use realAddr

【讨论】:

【参考方案2】:

你不能从用户空间访问页表,它们是在内核中映射的。

如果你在内核中,你可以简单地检查 CR3 的值来定位基页表地址,然后开始你的解析。

This blog series 对如何做到这一点有很好的解释。您不需要任何操作系统工具/API 来解析虚拟物理地址。

虚拟地址:f9a10054

1: kd> .formats 0xf9a10054
Binary:  11111001 10100001 00000000 01010100

Page Directory Pointer Index(PDPI)       11                        Index into

第一个表(页面目录指针 表) 页面目录索引(PDI) 111001 101 索引进入第二 表(页目录表)页 表索引(PTI) 00001 0000 索引到第三 表(页表)字节索引 0000 01010100 0x054,偏移量 进入物理内存页

在他的示例中,他们使用 windbg,!dq 是物理内存读取。

【讨论】:

【参考方案3】:

您的应用程序中有一个几乎连续的缓冲区。正如您所指出的,该虚拟内存范围仅在您的应用程序的上下文中可用,并且其中一些可能随时被调出。因此,为了从设备访问内存(也就是说,进行 DMA),您需要将其锁定并获取可以传递给设备的描述。

您可以通过使用 METHOD_IN_DIRECT 或 METHOD_OUT_DIRECT 向驱动程序发送 IOCTL(通过 DeviceControl 函数)来获取称为 MDL 或内存描述符列表的缓冲区的描述。有关定义 IOCTL 的讨论,请参见下一页。

http://msdn.microsoft.com/en-us/library/ms795909.aspx

现在您已经在设备的驱动程序中对缓冲区进行了描述,您可以将其锁定,以便缓冲区在您的设备可能对其进行操作的整个期间都保留在内存中。在 MSDN 上查找 MmProbeAndLockPages。

您的设备可能无法读取或写入缓冲区中的所有内存。该设备可能仅支持 32 位 DMA,并且机器可能有超过 4GB 的 RAM。或者您可能正在处理具有 IOMMU、GART 或其他一些地址转换技术的机器。为了适应这一点,请使用各种 DMA API 来获取一组适合您的设备使用的逻辑地址。在许多情况下,这些逻辑地址将等同于您的问题最初询问的物理地址,但并非总是如此。

您使用哪种 DMA API 取决于您的设备是否可以处理分散/收集列表等。您的驱动程序将在其设置代码中调用 IoGetDmaAdapter 并使用它返回的一些函数。

通常,您会对 GetScatterGatherList 和 PutScatterGatherList 感兴趣。您提供一个函数 (ExecutionRoutine),该函数实际上对您的硬件进行编程以进行传输。

涉及很多细节。祝你好运。

【讨论】:

【参考方案4】:

等等,还有更多。为了在您客户的 Vista 64 位上运行的特权,您将花费更多的时间和金钱来让您的内核模式驱动程序退出我的 Microsoft,

【讨论】:

【参考方案5】:

真的不应该在用户模式下做这样的事情;正如 Christopher 所说,您需要锁定页面,以便 mm 在设备使用它时不会决定调出您的后备内存,这最终会破坏随机内存页面。

但是,如果调用进程是驱动程序,并且我正在使用我的应用程序来调用该功能的驱动程序,那么我正在更改上下文并且在调用 mmGetPhysicalAddress 例程时我不再处于应用程序的上下文中

驱动程序不像用户模式应用那样具有上下文;如果您通过 IOCTL 或其他方式调用驱动程序,您通常(但不能保证!)处于调用用户线程的上下文中。但实际上,这与您的要求无关,因为无论您身在何处,内核模式内存(0x80000000 以上的任何内容)都是相同的映射,并且您最终会在内核端分配内存。但同样,编写一个合适的驱动程序。使用 WDF (http://www.microsoft.com/whdc/driver/wdf/default.mspx),它会让编写正确的驱动程序变得更加容易(虽然仍然很棘手,Windows 驱动程序编写并不容易)

编辑:只是想我会扔掉一些参考书来帮助您,您绝对应该(即使您不追求编写驱动程序)阅读 Russinovich 和 Solomon 的 Windows Internals (http://www.amazon.com/Microsoft-Windows-Internals-4th-Server/dp/0735619174/ref=pd_bbs_sr_2?ie=UTF8&s=books&qid=1229284688&sr=8-2);编写 Microsoft Windows 驱动程序模型也很好 (http://www.amazon.com/Programming-Microsoft-Windows-Driver-Second/dp/0735618038/ref=sr_1_1?ie=UTF8&s=books&qid=1229284726&sr=1-1)

【讨论】:

【参考方案6】:

在我的 C++ 程序(在 Windows 上)中,我正在分配一块内存,并且可以确保它在物理内存中保持锁定(未交换且连续)(即使用 VirtualAllocEx()、MapUserPhysicalPages() 等) .

不,您不能真正确保它保持锁定状态。如果您的进程崩溃或提前退出怎么办?如果用户杀死它怎么办?该内存将用于其他用途,如果您的设备仍在执行 DMA,最终将导致数据丢失/损坏或错误检查 (BSOD)。

另外,MapUserPhysicalPages 是 Windows AWE(地址窗口扩展)的一部分,用于在 32 位版本的 Windows Server 上处理超过 4 GB 的 RAM。我不认为它是用来破解用户模式 ​​DMA 的。

1.有什么方法可以在我的程序中以 USER 模式将虚拟地址转换为物理地址?

有一些驱动程序可以让您执行此操作,但您无法在 Windows 上从用户模式对 DMA 进行编程,并且仍然拥有稳定且安全的系统。让作为受限用户帐户运行的进程读/写物理内存允许该进程拥有系统。如果这是一次性系统或原型,这可能是可以接受的,但如果您希望其他人(尤其是付费客户)使用您的软件和设备,您应该编写一个驱动程序。

2.如果没有,我只能在 KERNEL 模式下找到这个虚拟到物理的映射。我想这意味着我必须编写一个驱动程序来做到这一点......?

这是解决此问题的推荐方法。

您是否知道我可以使用任何现成的驱动程序/DLL/API,我的应用程序(程序)将与之交互以进行翻译?

您可以使用MDL (Memory Descriptor List) 锁定任意内存,包括用户模式进程拥有的内存缓冲区,并将其虚拟地址转换为物理地址。您还可以使用METHOD_IN_DIRECTMETHOD_OUT_DIRECT 让Windows 为传递到DeviceIoControl 调用的缓冲区临时创建一个MDL。

请注意,虚拟地址空间中的连续页面在物理地址空间中几乎从不连续。希望您的设备能够处理这些问题。

3.如果我必须自己编写驱动程序,我该如何进行翻译?我使用哪些功能?是 mmGetPhysicalAddress() 吗?我该如何使用它?

编写驱动程序不仅仅是调用几个 API。如果您要编写驱动程序,我建议您从MSDN 和OSR 阅读尽可能多的相关材料。另外,请查看Windows Driver Kit 中的示例。

4.另外,如果我理解正确, mmGetPhysicalAddress() 返回调用进程上下文中的虚拟基地址的物理地址。但是,如果调用进程是驱动程序,并且我正在使用我的应用程序来调用该函数的驱动程序,那么我正在更改上下文并且在调用 mmGetPhysicalAddress 例程时我不再处于应用程序的上下文中......所以如何转换应用程序(用户模式)内存空间中的虚拟地址,而不是驱动程序?

驱动程序不是进程。驱动程序可以在任何进程的上下文以及各种提升的上下文(中断处理程序和 DPC)中运行。

【讨论】:

以上是关于如何将虚拟内存地址转换为物理地址?的主要内容,如果未能解决你的问题,请参考以下文章

如何把物理路径转换成虚拟路径

访问内存时如何通知操作系统内核?

将虚拟地址转换为物理地址

内存

虚拟地址转换为物理地址

Windows地址空间