将输入文件映射到内存中,然后直接从映射的内存页面解析数据,这是从文件中读取数据的一种方便有效的方法。
但是,除非您可以确保没有其他进程写入映射文件,否则这种做法似乎从根本上说是不安全的,因为即使私有只读映射中的数据可能会在另一个进程写入基础文件时发生更改。 (POSIX,例如doesn't specify“是否建立了MAP_PRIVATE映射后对基础对象的修改是通过MAP_PRIVATE映射”。)
如果您希望在对映射文件进行外部更改时使代码安全,则必须仅通过易失性指针访问映射内存,然后非常小心如何读取和验证输入,对于许多用例来说似乎不切实际。
这种分析是否正确?内存映射API的文档通常只在传递中提及这个问题,如果有的话,所以我想知道我是否遗漏了一些东西。
答案 0 :(得分:3)
这不是一个真正的问题。
是的,另一个进程可能会在您映射文件时对其进行修改,是的,您可能会看到修改。它甚至可能,因为几乎所有的操作系统都有统一的虚拟内存系统,所以除非有人请求无缓冲的写入,否则没有办法在没有经过缓冲区缓存的情况下编写,没有人没有持有映射看到变化。
这甚至都不是坏事。实际上,如果无法看到更改,那会更令人不安。由于文件准映射时会成为地址空间的一部分,因此您可以看到对文件所做的更改。
如果您使用传统的I / O(例如read
),有人仍然可以在您阅读时修改该文件。换句话说,将文件内容复制到内存缓冲区 在存在修改时始终是安全的。这是“安全的”,因为read
不会崩溃,但不保证您的数据一致。
除非您使用readv
,否则您无法保证原子性(即使使用readv
,您也无法保证内存中的内容与磁盘上的内容一致或者在内存之间不会发生变化。两次调用readv
)。有人可能会在两个read
操作之间修改文件,或者甚至在您处于中间位置时修改文件
这个不是只是没有正式保证但“可能仍然有效”的东西 - 相反,例如,在Linux下写的可证明不是原子的。甚至不是偶然的。
好消息:
通常,进程不只打开任意随机文件并开始写入它。当发生这样的事情时,它通常是属于进程的众所周知的文件(例如日志文件),或者是您明确告诉进程写入的文件(例如,保存在文本编辑器中),或者进程创建一个新文件(例如编译器创建一个目标文件),或者只是将附加到现有文件(例如db日志,当然还有日志文件)。或者,进程可能会自动将文件替换为另一个文件(或取消链接)。
在每种情况下,整个可怕的问题归结为“没有问题”,因为要么你很清楚会发生什么(所以这是你的责任),要么它无缝地工作而不会干扰。
如果您真的不喜欢另一个进程可能在映射时可能写入文件的可能性,那么您可以在创建文件时在Windows下省略FILE_SHARE_WRITE
处理。 POSIX使它变得有点复杂,因为您需要fcntl
强制锁的描述符,这在每个系统上都不是必需的,或者是100%可靠的(例如,在Linux下)。
答案 1 :(得分:1)
理论上,如果有人这样做,你可能会陷入困境 在阅读文件时修改文件。在实践中:你是 阅读人物,没有别的:没有指针,或任何东西 这可能会让你陷入困境。在实践中...正式地, 我认为它仍然是未定义的行为,但它是一个 我认为你不必担心。除非修改 是非常小的,你会得到很多编译器错误,但那是 关于它的结束。
可能导致问题的一个案例是文件是否 缩短。当你正在阅读时,我不确定会发生什么 超越结束。
最后:系统不是随意打开的 修改文件。这是一个源文件;这将是一个白痴 做这件事的程序员,他应得的。没有 如果你的未定义行为破坏系统或其他 人民档案。
另请注意,大多数编辑都在使用私人副本;当。。。的时候
回写,他们通过重命名原始和创建来实现
一个新文件。在Unix下,将文件打开到mmap
后
它,重要的是inode数量。当编辑
重命名或删除文件,您仍然保留您的副本。该
修改后的文件将获得一个新的inode。你唯一要做的事情
担心的是,如果有人打开文件进行更新,那么
四处修改它。没有多少程序在文本上执行此操作
文件,除了将附加数据附加到最后。
因此,虽然正式,但存在一些风险,我认为你不必这样做
担心它。 (如果你真的很偏执,你可以关掉
在mmap
编辑时写入授权。如果有的话
真的是一个敌人的代理人,你可以把它转回来
上。)