Windows 操作系统如何跟踪进程的虚拟内存?

Windows 操作系统如何跟踪进程的虚拟内存?

背景: 所以我知道在现代操作系统中,一个进程基本上有一个完整的 32 位(或 64 位)地址空间可供使用。据我所知,进程的实际虚拟内存地址首先存储在程序的 .exe 文件中。这意味着链接器将用实际地址(如 0x00007fb6)修补所有函数引用和变量。据我了解,这些虚拟地址(或由于加载器而修改的版本)最终可以映射到 RAM 中的实际物理地址。

问题: 我的问题是,当作者谈论将某些内容映射到程序的虚拟地址空间(例如映射文件内容)时,他们到底是什么意思?据我所知,操作系统从 .exe 获取单个进程的虚拟地址,因此如果另一个文件的内容被映射到该进程的虚拟地址空间,那么这些内容是否会保存回 exe 文件?或者操作系统只是以某种方式在后台跟踪添加的内容?

答案1

首先,如果你想深入了解这类事情,我强烈建议你拿一份Windows Internals,第 1 部分:系统架构、进程、线程、内存管理等,第 7 版。它很好地解释了细节。或者,看看例如Linux 文档,它是更公开的 - 它会有些不同,但遵循与 Windows 相同的许多概念。

这里是另一个或许更容易理解的解释。

你可能还想看看页表工作,也许还有层级缓冲器


它的要点是,当一个进程尝试读取或写入特定虚拟地址空间,CPU 的内存管理单元 (MMU) 要么将其转换为物理地址(通过 TLB(一种专门用于页表条目的特殊片上缓存)查看页表),要么在页表条目显示“页面不存在”时通知操作系统。这就是所谓的页面错误作为响应,操作系统 a) 如果必要,为进程分配一个新的物理页面,并将其内容从“后备存储”(页面文件或映射文件)加载到该 RAM 页面中,b) 更新页表条目以反映页面的物理地址,以及 c) 告诉 CPU 再试一次(“消除页面错误”)。

对于定义的虚拟地址空间的每个页面,都有一个页表条目。页表条目不仅由 CPU 用于执行地址转换。对应于“无效”页面(访问时会导致页面错误的页面)的 PTE 被操作系统用于存储有关页面数据位置的信息。因此,所有定义的虚拟地址都存在 PTE。一个子集通常是“有效的”,即它们对应于可以不发生页面错误的虚拟地址。(这个子集称为进程的“工作集”。)

“无效”页面的 PTE 包含信息,当该页面发生故障时,操作系统可以通过这些信息找到页面的内容。内容可能位于页面文件中、映射文件中、RAM 中(在其中一个页面缓存中)、另一个进程的工作集中(对于共享页面),或者有时根本不存在(对于尚未定义初始内容且尚未引用的页面;此类页面只是物理分配并在首次引用时填充零;这些事件称为“请求零页面错误”)。

页表太大,无法完全放入(非常非常小的)TLB 中,这就是为什么它们只是存储在普通内存中。

事实上,页表太大,无法一次性全部放入 RAM 中,因此它们按每个进程以树结构进行组织,并且除了每棵树的顶层表之外的所有内容本身都是可分页的。

在典型的现代操作系统中,每个进程都有自己独立的页表,使进程的地址空间彼此独立。内核在其自己的内存区域内跟踪进程及其地址空间。在当前的 Windows 中,它与其他进程元数据一起存储在内存中的“ EPROCESS”数据结构和称为虚拟地址描述符的相关结构中。Windows 还为其自己的内核模式代码和数据维护单独的页表结构。

需要记住的是:进程的地址空间完全存储在内存中,绝不会写回到启动进程的可执行文件 + DLL。进程退出时,地址空间将被丢弃。但是,对已打开以进行写访问的映射文件的修改将刷新回其各自的文件。


据我所知,进程的实际虚拟内存地址首先存储在程序的 .exe 文件中。这意味着链接器将用实际地址(例如 0x00007fb6)修补所有函数引用和变量。

不。这仅用于对程序代码的引用;进程的地址空间还包括两个或多个堆、每个线程的堆栈以及进程的线程在运行时碰巧创建的任何其他地址范围。

如今,甚至程序代码在加载时也会(随机)重新排列;这被称为 ASLR,它使某些类型的攻击变得更加困难。

据我所知,操作系统从 .exe 中获取单个进程的虚拟地址

不。可执行文件仅指定一些初始数据的位置;即程序代码、全局变量和链接库。而且在现代操作系统中,这些信息中的大部分都被 ASLR 覆盖。

其他内存(程序堆、线程堆栈、映射文件等)由程序动态分配 - 它们的大小、位置等可以在程序执行期间发生变化。

如上所述,每个进程都有自己独立的虚拟地址空间。其中一部分映射到内核内存(标记为不可从用户模式读取或修改),但其余部分是进程私有的。除非明确请求(“共享内存”)或为了提高性能而完全透明地执行(写时复制、映射文件),否则进程的地址空间不会与其他进程共享。

图 5-10x86 虚拟地址空间布局(左侧 2 GB,右侧 3 GB)来自Windows Internals,第 1 部分:系统架构、进程、线程、内存管理等,第 7 版是一个很好的图表,显示了一般进程地址空间布局。不幸的是,我不能在这里重现它。

因此如果另一个文件的内容被映射到该进程的虚拟地址空间

这最终会成为进程页表中的一个特殊条目,告知操作系统从映射文件加载数据。这实际上是一个重定向;“到别处看看”。请记住,地址空间是虚拟的- 它可能引用一个文件,但该文件不会加载,直到有线程尝试读取/写入它。即使这样,它也只会一次加载一页或几页。

那么这些内容会被保存回 exe 文件中吗

否。可执行文件始终以只读方式加载。加载后,所有相关信息都纯粹存储在内存中,操作系统永远不会“保存回”。进程退出时,对进程内存的任何更改都会丢失,但对映射文件或共享内存的写入除外。

或者操作系统只是以某种方式在后台跟踪添加的内容?

是的,在流程元数据中跟踪哪些区域映射到哪个文件。

相关内容