丢弃脏的写时复制页面

4
有没有一种方法可以通过MAP_PRIVATE创建一个写时复制映射,写入一些数据(即脏页),然后丢弃我的更改,而不使用munmap和重新映射?目标是保持给定映射的相同虚拟地址(如果我取消映射并再次映射相同的文件,则无法保证发生这种情况),但一次性丢弃所有的COW更改。
据我所知,尝试通过暗示地址并使用MAP_FIXED标志重新映射空间可能会产生这种效果;但我不确定我的MAP_FIXED文档解释是否正确,或者是否保证了这种行为。
引用来自mmap(2)文档:
If the memory region specified by addr and len overlaps pages of any existing
mapping(s), then the overlapped part of the existing mapping(s) will be 
discarded.

在这种情况下,“被丢弃”是否意味着任何COW页面都将被丢弃,而从相应页面读取的新内容将会失败并反映磁盘上的更改?

2个回答

3
如果进行重叠的操作,Linux内核将像对它们执行一样清除现有映射的重叠部分。例如,如果你映射了一个共享库曾经使用过的帧缓冲区,那么该内存现在与该共享库毫无关系;它指向帧缓冲区。被移除映射的底层页面对象独立于映射存在:页面是引用计数对象。当两个映射共享相同页面的视图时,仅是由于相同页面在不同视图中被“安装”。当页面变脏并解除映射时,这不会创建一个依赖关系,使得必须在新映射之前写入脏页面;虚拟内存可以已经重新分配给新映射(例如图形帧缓冲区的一部分),然后原始脏页(例如文件支持的共享映射的一部分)被刷新。
关于丢弃映射的问题:我认为你不能这样做。也就是说,如果你有一个映射,它应该将脏页刷新到底层文件中,你不能快速地写入该内存,然后希望它永远不会被完成,然后快速地它(或者在其上其他内容)。在Linux的 API中,有一个看起来相关的操作,但根据手册页,它似乎只适用于和。我认为阻止写入发生的唯一方法是进行被称为“跳闸”的历史悠久的仪式。
有一种映射文件对象的方法,使得更改不会传播:即(相对于)。例如,调试器(如)需要能够将断点放在可执行文件或共享库中,而不会在每个运行进程中的该可执行文件或库的每个实例(及磁盘上的副本!)中引发陷阱指令,因此需要。如果使用了修改后的,并且你它(或者这些部分),或者在它们之上映射其他内容,则我认为它们将被丢弃。这些页面应该已经按写时复制方式进行处理,因此使它们变脏的进程应该保持唯一的引用。当它们被解除映射时,它们的引用计数就会降为零,并且由于它们是私有页,它们就会被清除。

非常有趣。所以,如果我1)在文件上创建一个MAP_PRIVATE映射,2)将其读入页面缓存,3)通过在该私有的COW映射中写入脏页,4)重新映射带有MAP_FIXED标志,覆盖MAP_PRIVATE映射的地址空间,则我应该保留底层文件在页面缓存中的“真实”数据,同时丢弃我对COW“更改”的视图。这是非常方便的行为。 - Bryce
Bryce,是的;我相信文件中原始数据应该会重新出现;如果您将lseek到这些位置并进行read操作,您将看到相同的数据。 - Kaz

1
虚拟地址在复制后仍然保持不变。只有物理地址会改变(以及相关的内存映射页寄存器)。
在进程写入页面后,撤销已经为时已晚。复制发生在第一次写入内存区域时。

我明白我的第一份副本实际上会写入物理RAM,而且我无法“撤消”它 - 但问题是,使用重叠地址的新mmap(或其他机制)是否会导致相同的虚拟内存以一种方式被“重新初始化”,从映射过程的角度来看抛弃那些更改。 - Bryce

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接