可执行目标文件和虚拟内存

Executable Object Files and Virtual Memory

我是 Linux 和虚拟内存的初学者,仍在努力理解虚拟内存和 Executable 对象文件之间的关系。

假设我们有一个 executable 目标文件 a.out 存储在硬盘上,假设最初 a.out 有.data 部分包含一个值为 2018 的全局变量。 当加载程序 运行 时,它分配一个连续的虚拟页面块,将它们标记为无效(即未缓存)并将它们的页面 table 条目指向 a.out 中的适当位置。加载程序实际上从不将任何数据从磁盘复制到内存中。数据在第一次引用每个页面时由虚拟内存系统按需自动分页。

我的问题是:假设程序在 运行 时间将全局变量的值从 2018 更改为 2019 并且似乎包含全局变量的虚拟页面最终将页面调出到磁盘,这意味着 .data 部分的全局变量现在为 2019,因此我们更改了不应该更改的 executable 目标文件?否则我们每次完成并再次 运行 程序时都会得到不同的值?

一般(不是专门针对 Linux)...

当 executable 文件启动时,OS(内核)创建一个虚拟地址 space 和一个(最初为空)进程,并检查 executable 文件的 header。 executable 文件的 header 描述了 "sections"(例如 .text.rodata.data.bss 等),其中每个部分具有不同的属性——该部分的内容是否应该放在虚拟地址 space 中(例如,是一个符号 table 或在 run-time 处未使用的东西),如果内容是否是文件的一部分(例如 .bss),如果该区域应该是 executable、read-only 或 read/write.

通常,executable 文件(使用的部分)由虚拟文件系统缓存;并且已经在 VFS 缓存中的文件片段可以映射(如 "copy on write")到新进程的虚拟地址 space。对于不在 VFS 缓存中的部分,文件的这些部分可以映射为 "need fetching" 到新进程的虚拟地址 space.

然后进程开始(给定 CPU 时间)。

如果进程从尚未加载的页面读取数据; OS(内核)暂停进程,从磁盘上的文件中获取页面到 VFS 缓存中,然后还将页面映射为 "copy on write" 到进程中;然后允许进程继续(允许进程重试从未加载的页面读取,现在页面已加载,这将起作用)。

如果进程写入的页面仍然是"copy on write"; OS(内核)暂停进程,分配一个新页面并将原始页面的数据复制到其中,然后用进程自己的副本替换原始页面;然后允许进程继续(允许进程重试写入,因为进程有自己的副本,现在可以正常工作)。

如果进程从尚未加载的页面写入数据; OS(内核)结合了之前的两件事(从磁盘获取原始页面到 VFS 缓存,创建一个副本,将进程的副本映射到进程的虚拟地址 space)。

如果 OS 开始 运行 可用内存不足;然后:

  • VFS 缓存中但未与任何进程共享 "copy on write" 的文件数据页可以在 VFS 中释放,无需执行任何其他操作。下次使用该文件时,这些页面将从磁盘上的文件中提取到 VFS 缓存中。

  • VFS 缓存中的
  • 页文件数据也可以作为 "copy on write" 与任何进程共享,可以在 VFS 中释放,any/all 进程中的副本标记为作为 "not fetched yet"。下次使用该文件时(包括进程访问 "not fetched yet" page/s 时),这些页面将从磁盘上的文件中提取到 VFS 缓存中,然后映射为 "copy on write" process/es).

  • 页数据已被修改(因为它们最初是 "copy on write" 但被复制了,或者因为它们根本不是 executable 文件的一部分- 例如 .bss 部分,executable 的堆 space,等等)可以保存到交换 space 然后释放。当进程再次访问 page/s 时,它们将从交换 space.

  • 中获取

注意:如果 executable 文件存储在不可靠的媒体上(例如可能有划痕的 CD),"smarter than average" OS 可能会将整个 executable 文件加载到VFS 缓存 and/or 最初交换 space;因为除了使进程崩溃(例如 SIGSEGV)并使其看起来像 executable 时,没有其他理智的方法来处理 "read error from memory mapped file" 进程正在使用文件不是,因为这提高了可靠性(因为您依赖于更可靠的交换而不是依赖于不太可靠的划伤 CD)。还;如果 OS 防止文件损坏或恶意软件(例如,在 executable 文件中内置了 CRC 或数字签名),那么 OS 可能(应该)将所有内容加载到内存中(VFS 缓存)在允许执行 executable 之前检查 CRC 或数字签名,以及(对于安全系统,以防磁盘上的文件在 executable 为 运行ning 时被修改)释放 RAM 时可能会将未修改的页面存储在 "more trusted" swap space 中(与修改页面时的情况相同)以避免从原始 "less trusted" 文件中获取数据(部分原因是您不每次从文件加载页面时都想进行整个数字签名检查。

My question is: suppose the program change the value of global variable from 2018 to 2019 on the run time and it seems that the virtual page that contains the global variable will eventually page out to the disk, which means that .data section has the global variable to be 2019 now, so we change the executable object file which are not supposed to be changed?

包含 2018 的页面将以 "not fetched" 开始,然后(当它被访问时)加载到 VFS 缓存中并映射到进程中作为 "copy on write"。在任何一个点,OS 都可以释放内存并在需要时从磁盘上的 executable 文件中获取数据(尚未更改)。

当进程修改全局变量(将其更改为包含 2019)时,OS 会为进程创建一个副本。在这一点之后,如果 OS 想要释放内存 OS 需要将页面的数据保存在交换 space 中,并加载页面的ata 从 swap space 返回,如果它被再次访问。 executable 文件没有被修改并且(对于那个页面,对于那个进程)executable 文件没有被再次使用。