我正在考虑一个系统,它可以让我存储映射文件并透明地对它们包含的数据进行类型转换。似乎可以通过映射第二个内存区域并使其受到保护来捕获内存访问,然后在访问新页面时捕获段错误。这将让我处理我需要的读取类型转换。
但是,为了兼容读/写,我需要一些方法来捕捉操作系统何时将部分内存分页回磁盘,这样我就可以在写入之前以另一种方式进行类型转换。
有没有以这种方式挂钩分页系统的能力?
你想要的东西是不可能的,这反映了对mmap
. 文件支持的映射被写回磁盘的事件是不相关的,因为在这种情况发生之前,任何读取文件的尝试都将(并且必须,以符合 POSIX)从页面的已修改内存副本中读取,不是磁盘上过时的内容。换句话说,将修改后的页面写回磁盘对应用程序来说是完全透明的,并且假设您永远不会断电或重新启动,那么修改后的页面完全有可能永远不会写回磁盘。
你的设计是行不通的。如果你想要这种行为,你将不得不做一些不同的事情。
使用内存映射和 SIGSEGV 处理程序有点问题。首先,mprotect()不是异步信号安全的,这意味着mprotect()
在信号处理程序中不能保证工作。其次,信号处理程序和多个线程之间必要结构的同步非常复杂(尽管可以使用 GCC __sync和/或__atomic内置函数),因为您不能在信号处理程序中使用标准锁定原语——幸运的是,您可以简单地从信号处理程序返回;内核不会跳过有问题的指令,因此之后会立即发出相同的信号。
我确实编写了一个小程序来测试匿名私有非保留内存映射,使用read()
并write()
更新映射。问题是其他线程可能会在信号处理程序更新映射时访问映射。
我认为如果您为当前活动区域使用临时文件,当记录跨越页面边界时,在前后有一个额外的页面来保存部分记录,它可能会起作用。
实际数据文件将由私有匿名未保留不可访问映射 ( PROT_NONE
, MAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE
) 表示。SIGSEGV 信号处理程序捕获对该映射的访问。该映射的页面对齐区域未映射并从临时文件 ( MAP_SHARED | MAP_FIXED | MAP_NORESERVE
) 映射。诀窍是临时文件可以额外映射(MAP_SHARED | MAP_NORESERVE
)到另一个内存区域,信号处理程序可以简单地在映射中取消映射临时文件,以阻止其他线程在转换期间访问该区域;数据仍然可用于另一个内存区域中的库函数(要从实际数据文件中读取和写入read()
) 。意味着使用完全相同的页面(来自页面缓存),并且意味着内核不会为它们保留交换或 RAM。write()
MAP_SHARED
MAP_NORESERVE
这种方法在线程和锁定方面应该可以很好地工作,但它仍然存在mmap()
, munmap()
, 并且mremap()
不是异步信号安全的问题。但是,如果您确实有一个仅以原子方式访问的全局变量导致信号处理程序在应用程序/库代码正在修改结构和/或映射时立即返回,那么这应该是可靠的。