我是Linux和虚拟内存的初学者,但仍在努力理解虚拟内存和可执行对象文件之间的关系。
假设我们在硬盘驱动器磁盘上存储了一个可执行对象文件 a.out ,并假设a.out最初具有一个 .data 部分,其中值为2018的变量。 加载程序运行时,它将分配连续的虚拟页块,将其标记为无效(即未缓存),并将其页表项指向a.out中的适当位置。加载程序实际上从未将任何数据从磁盘复制到内存中。第一次引用每页时,虚拟内存系统会根据需要自动分页数据。
我的问题是:假设程序在运行时将全局变量的值从2018更改为2019,看来包含该全局变量的虚拟页面最终将分页到磁盘,这意味着 .data 部分的全局变量现在为2019,因此我们更改了不应该更改的可执行对象文件吗?否则,每次完成并再次运行程序时,我们都会获得不同的值?
答案 0 :(得分:1)
通常(不是专门针对Linux)...
启动可执行文件后,操作系统(内核)将创建虚拟地址空间和(最初为空)进程,并检查可执行文件的标头。可执行文件的标题描述“节”(例如.text
,.rodata
,.data
,.bss
等),其中每个节都有不同的属性-如果节的内容应是否将其放置在虚拟地址空间中(例如,符号表或在运行时未使用的内容),如果内容是否属于文件的一部分(例如,.bss
),以及该区域应该是可执行的,只读的或读/写的。
通常,可执行文件(的使用部分)由虚拟文件系统缓存;并且可以将VFS缓存中已存在的文件片段映射(作为“写入时复制”)到新进程的虚拟地址空间中。对于尚未在VFS缓存中的部分,可以将文件的那些部分映射为“需要获取”到新进程的虚拟地址空间中。
然后启动进程(给定CPU时间)。
如果进程从尚未加载的页面读取数据; OS(内核)暂停该过程,将页面从磁盘上的文件中提取到VFS缓存中,然后还将页面作为“写时复制”映射到该过程中;然后允许该过程继续进行(允许该过程重试从未加载的页面上的读取,现在该页面已加载就可以使用。)
如果进程写入的页面仍然是“写入时复制”; OS(内核)暂停进程,分配一个新页面并将原始页面的数据复制到其中,然后用该进程自己的副本替换原始页面;然后允许该进程继续执行(允许该进程重试该写操作,因为该进程具有其自己的副本,该写操作将继续工作)。
如果进程从尚未加载的页面写入数据; OS(内核)结合了之前的所有内容(将磁盘中的原始页面提取到VFS缓存中,创建副本,将进程的副本映射到进程的虚拟地址空间)。
如果操作系统开始耗尽可用内存;然后:
页,而无需执行其他任何操作。下次使用该文件时,这些页面将从磁盘上的文件中提取到VFS缓存中。
页,并且可以在标记为“尚未提取”的任何/所有进程中释放副本。 。下次使用该文件时(包括当进程访问“尚未提取”的页面时),这些页面将从磁盘上的文件中提取到VFS缓存中,然后在进程中映射为“写入时复制” )。
页数据(要么是因为它们最初是“写时复制”但已被复制,要么是因为它们根本不属于可执行文件的一部分,例如.bss
部分,可执行文件的堆空间等)可以保存为交换空间,然后释放。当进程再次访问页面时,将从交换空间中获取它们。
注意:如果可执行文件存储在不可靠的介质(例如,可能被刮擦的CD)上,则“比平均水平更聪明”的操作系统可能会首先将整个可执行文件加载到VFS缓存和/或交换空间中;因为在进程使用文件时,除了使进程崩溃(例如SIGSEGV
)并使其看上去好像可执行文件有错误之外,没有其他明智的方式来处理“从内存映射文件读取错误”,并且因为这样可以提高可靠性(因为您依赖的是更可靠的交换,而不是依赖于可靠性不高的暂存CD)。也;如果OS防止文件损坏或恶意软件(例如,在可执行文件中内置了CRC或数字签名),则OS可以(应)将所有内容加载到内存(VFS缓存)中以检查CRC或数字签名,然后再允许可执行文件被执行。执行,以及(对于安全系统,如果可执行文件在运行时修改了磁盘上的文件)释放RAM时,可能会将未修改的页面存储在“更受信任”的交换空间中(与修改页面时相同),以避免从原始的“不受信任”文件中获取数据(部分原因是您不想每次从文件加载页面时都进行整个数字签名检查)。
我的问题是:假设程序在运行时将全局变量的值从2018更改为2019,看来包含全局变量的虚拟页面最终将分页到磁盘,这意味着.data节全局变量现在是2019,所以我们更改了不应该更改的可执行目标文件?
包含2018
的页面将以“未获取”开始,然后(在其被访问时)加载到VFS缓存中,并映射为“写时复制”到进程中。在这两种情况下,操作系统都可以释放内存,并在再次需要时从磁盘上的可执行文件中获取数据(尚未更改)。
当进程修改全局变量(将其更改为包含2019
时),操作系统将为该进程创建一个副本。此后,如果操作系统要释放内存,则操作系统需要将页面数据保存在交换空间中,并在再次访问该页面数据时将其从交换空间加载回去。可执行文件未修改,并且(对于该页面,该进程)不再使用该可执行文件。