但是,除非您可以确保没有其他进程对映射文件进行写入,否则这种做法也似乎是根本不安全的,因为如果底层文件由另一个进程写入,即使私有只读映射中的数据可能会更改. (POSIX例如doesn’t specify“是否可以通过MAP_PRIVATE映射查看在MAP_PRIVATE映射建立之后完成的底层对象的修改”.)
如果您希望在映射文件存在外部更改的情况下使代码安全,则只能通过volatile指针访问映射的内存,然后非常小心您如何读取和验证输入,这似乎不切实际很多用例
这个分析是否正确?内存映射API的文档通常只提及这个问题,如果有的话,我想知道我是否缺少一些东西.
解决方法
是的,另一个进程可能会在映射文件时修改文件,是的,可能会看到修改.由于几乎所有的操作系统都具有统一的虚拟内存系统,因此几乎所有的操作系统都是可以统一的,所以除非一个请求无缓冲的写入,否则没有办法通过缓冲区缓存进行写入,没有任何人没有看到变化的映射.
那甚至不是坏事.其实,如果你看不到这些变化,会更加令人不安.由于在映射文件时,文件准将成为地址空间的一部分,因此您可以看到文件的更改是完美的.
如果使用传统的I / O(如读取),则在读取文件时仍可修改文件.用不同的措辞,将文件内容复制到内存缓冲区并不总是存在修改的安全性.只要读取不会崩溃,它是“安全的”,但不能保证您的数据是一致的.
除非你使用readv,否则你不能保证原子性(甚至在readv你不能保证你在内存中的内容与磁盘上的一致,或者在两次调用readv之间都不会改变).有人可能会在两次读取操作之间修改文件,或者甚至当您处于中间时.
这不仅仅是一个没有得到正式保证的东西,而是“可能仍然有效” – 相反地,例如在Linux下的写作不是原子的.甚至偶然.
好消息:
通常,进程不只是打开任意的随机文件并开始写入它.当这样的事情发生时,它通常是属于进程的一个众所周知的文件(例如日志文件),或者你明确地告诉进程写入的文件(例如保存在文本编辑器中),或者进程创建一个新的文件(例如编译器创建一个对象文件),或者这个进程只是附加到一个现有的文件(例如数据库日志,当然还有日志文件).或者,进程可能会原子替换另一个文件(或取消链接).
在每一种情况下,整个可怕的问题归结为“没有问题”,因为您很清楚将会发生什么(因此是您的责任),否则无缝干扰就可以工作.
如果您真的不喜欢另一个进程可能在映射文件时写入文件的可能性,那么在创建文件句柄时,您可以在Windows下简单地省略FILE_SHARE_WRITE. POSIX使得它更复杂一些,因为您需要为一个强制性锁定描述符fcntl,这在每个系统(例如,在Linux下)不需要支持或100%可靠.