【问题标题】:Catching when the linux kernel writes a page back to a memory mapped file?捕获 linux 内核将页面写回内存映射文件的时间?
【发布时间】:2012-07-26 20:41:16
【问题描述】:

我正在考虑一个系统,它可以让我存储映射文件并透明地对它们包含的数据进行类型转换。似乎可以通过映射第二个内存区域并使其受到保护来捕获内存访问,然后在访问新页面时捕获段错误。这可以让我处理我需要的读取类型转换。

但是,为了读/写兼容,我需要一些方法来捕捉操作系统何时将部分内存分页回磁盘,以便在写入之前以另一种方式进行类型转换。

有没有以这种方式挂钩分页系统的能力?

【问题讨论】:

  • 你想做什么听起来类似于 encfs,它使用 FUSE 提供文件加密。它必须能够在读取时解密并在写入时加密。也许您可以使用 encfs 作为指南来制作自己的 FUSE 文件系统。
  • 现在有一个想法,我会调查一下。它基本上归结为同一件事。我只是想以不同的方式转换数据。 (编辑)哦,但我只是想我不想将我的文件存储在一个特殊的地方来获得这种行为,我希望它适用于任何文件,比如过滤器。不过我还是会看看的。
  • 您的 FUSE fs 可以在本机 fs 上指定一个目录名称作为挂载选项,然后将所有内容传递给它,让较低级别的 fs 像往常一样与块设备通信。堆叠!我认为这就是 encfs 的工作原理。

标签: c linux mmap


【解决方案1】:

你想要的东西是不可能的,这反映了对mmap 的根本误解。文件支持的映射被写回磁盘的事件是不相关的,因为在这种情况发生之前,任何读取文件的尝试都将(并且必须,以符合 POSIX)从修改后的- 页面的内存副本,而不是磁盘上过时的内容。换句话说,将修改后的页面写回磁盘对应用程序来说是完全透明的,并且假设您永远不会断电或重新启动,那么修改后的页面完全有可能永远写回磁盘。

你的设计是行不通的。如果你想要这种行为,你必须做一些不同的事情。

【讨论】:

  • 我当然明白,但是 if 页面 is 已交换回磁盘,对于我想做的工作,我必须拦截那,输入convert,然后将that 类型转换后的数据写回磁盘。完成后我将调用 munmap 以保证将任何修改后的数据写回磁盘。
  • 不,你不明白。 munmap 不保证将数据写回磁盘。它所做的只是从进程的虚拟地址空间中删除映射。 “存储在磁盘上”的概念在 POSIX 系统上根本不存在。通过mmap-obtained 映射进行的任何写入对于通过任何方式读取文件的任何进程(无论是read 还是另一个mmap立即可见。
  • 好吧,肯定有一个 msync() 函数确实可以保证将更改写回,所以我也会调用它。
  • 而且您似乎与您现在在这里所宣称的完全相反:stackoverflow.com/questions/5902629/…
  • 对不起,我想也许我解释得不好。我想内存映射一个文件,然后创建第二个匿名内存映射区域来代理访问内存映射文件以进行类型转换。我可以通过保护匿名到达并在其上捕获 SIGSEGV 来处理其中的读取部分,但它正在以另一种方式进行,并且实际上写入我想知道是否可以以某种方式处理的第二个映射区域。
【解决方案2】:

使用内存映射和 SIGSEGV 处理程序有点问题。首先,mprotect() 不是async-signal safe,这意味着信号处理程序中的mprotect() 不能保证工作。其次,信号处理程序和多个线程之间必要结构的同步非常复杂(尽管可以使用 GCC __sync 和/或__atomic 内置函数),因为您不能在信号处理程序中使用标准锁定原语——幸运的是,您可以简单地从信号处理程序返回;内核不会跳过有问题的指令,因此之后会立即发出相同的信号。

我确实编写了一个小程序来测试匿名私有未保留内存映射,使用read()write() 来更新映射。问题是在信号处理程序更新映射时,其他线程可能会访问映射。

我认为,如果您为当前活动区域使用临时文件,当记录跨越页面边界时,在前后有一个额外的页面来保存部分记录,它可能会起作用。

实际的数据文件将由一个私有匿名无保留不可访问映射(PROT_NONEMAP_ANONYMOUS | MAP_PRIVATE | MAP_NORESERVE)表示。 SIGSEGV 信号处理程序捕获对该映射的访问。该映射的页面对齐区域未映射并从临时文件 (MAP_SHARED | MAP_FIXED | MAP_NORESERVE) 映射。诀窍是临时文件可以额外映射(MAP_SHARED | MAP_NORESERVE)到另一个内存区域,信号处理程序可以简单地在映射中取消映射临时文件,以阻止其他线程在转换期间访问该区域;数据仍然可用于另一个内存区域中的库函数(使用read()write() 读取和写入实际数据文件)。 MAP_SHARED 表示使用完全相同的页面(来自页面缓存),MAP_NORESERVE 表示内核不为它们保留交换或 RAM。

这种方法在线程和锁定方面应该可以很好地工作,但它仍然受到mmap()munmap()mremap() 不安全的异步信号的影响。但是,如果您确实有一个全局变量只能以原子方式访问,从而导致信号处理程序在应用程序/库代码正在修改结构和/或映射时立即返回,那么这应该是可靠的。

【讨论】:

  • 感谢 cmets。我想我会限制自己使用来自单个线程(主线程)的内存映射区域。我曾考虑过同步问题,并认为这不值得麻烦。 mprotect 不是信号安全是令人担忧的。虽然正如这里提到的:stackoverflow.com/questions/2663456/… 在 linux 上它应该是安全的,这就是我将生活的地方。
  • 我昨天没有意识到,但是如果你使用单独的线程和sigwaitinfo()signalfd() 来接收 SIGSEGV 和 SIGBUS 信号,同时使用 sigprocmask() 阻止所有其他线程,应该没有任何问题。请记住,只要您确保足够早和足够晚地使用mprotect(),如果存在竞争条件,这两个信号都将简单地重新发出。库状态可能仅限于该线程,您可以使用pthread_mutex_t 进行锁定。你需要真正的工作示例代码吗? (我只需要清理我的测试代码。)
猜你喜欢
  • 2017-10-19
  • 1970-01-01
  • 2014-02-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-12-22
  • 1970-01-01
  • 2015-10-05
相关资源
最近更新 更多