用于读取输入文件的内存映射文件的安全

How safe are memory-mapped files for reading input files?

本文关键字:文件 映射 安全 内存 读取 用于 输入      更新时间:2023-10-16

将输入文件映射到内存中,然后直接从映射的内存页面解析数据可能是一种从文件中读取数据的方便而有效的方法。

但是,除非您可以确保没有其他流程写入映射的文件,否则这种做法似乎也从根本上是不安全的,因为即使是私人读取的映射中的数据,如果基本文件是由另一个过程写给另一个过程的。(例如,POSIX没有指定"通过MAP_PRIVATE映射可以看到MAP_PRIVATE映射后完成的基础对象的修改"。)

如果您想在对映射文件的外部更改的情况下确保代码安全,则必须仅通过挥发性指针访问映射的内存,然后非常谨慎地仔细阅读和验证输入,哪个对于许多用例,似乎是不切实际的。

此分析正确吗?内存映射API的文档通常仅在传递中提到此问题,因此我想知道我是否缺少某物。

它不是真的是一个问题。

是的,另一个过程可能在您映射文件时修改该文件,是的,您可能会看到修改。它甚至可能可能,因为几乎所有操作系统都有统一的虚拟内存系统,因此,除非有人请求未封闭的写入,否看到变化。
这甚至不是一件坏事。实际上,如果您无法看到更改,那将更加令人不安。由于映射时文件quasi成为地址空间的一部分,因此您可以看到对文件的更改是非常有意义的。

如果您使用常规I/O(例如read),则在阅读该文件时仍然可以修改该文件。用不同的方式措辞,将文件内容复制到内存缓冲区是不是在存在修改的情况下始终安全的。在read不会崩溃的情况下,它是"安全的",但它确实可以保证您的数据一致。
除非您使用readv,否则您无法保证原子能(即使使用readv,您也无法保证您的内存中所拥有的与磁盘上的内容一致,或者它不会在对readv的两个调用之间变化)。有人可能会在两个read操作之间修改文件,甚至在您处于中间的中间。
不是只是无法正式保证的东西,而是"可能仍然有效" - 相反,例如在Linux下,写作是明显的不是原子。甚至没有偶然。

好消息:
通常,过程只需打开一个任意的随机文件即可开始写入。当发生这种事情时,通常是属于该过程的知名文件(例如日志文件),或者您明确告诉该过程写入的文件(例如保存在文本编辑器中)或该过程创建一个新文件(例如编译器创建对象文件)或该过程仅将附加到现有文件(例如DB期刊,当然还有日志文件)。或者,一个过程可能会原子能用另一个文件替换文件(或取消链接)。

在每种情况下,整个可怕的问题都归结为"无问题",因为您要么很好地知道会发生什么(因此是您的责任),或者它在不干扰的情况下无缝地工作。

如果您真的不喜欢其他过程在映射时可能会写入到您的文件时的可能性,则可以在创建文件句柄时只需省略Windows下的FILE_SHARE_WRITE即可。POSIX使它变得更加复杂,因为您需要fcntl强制性锁定的描述符,这不需要支持或在每个系统上可靠100%(例如,在Linux下)。

从理论上讲,如果有人这样做,您可能会遇到麻烦在阅读文件时修改文件。实际上:你是阅读字符,什么也没有:没有指针或任何东西这可能会让您陷入困境。在实践中...正式,我认为这仍然是不确定的行为,但这是我认为您不必担心。除非修改很小,您会遇到很多编译器错误,但这就是关于它的结尾。

可能引起问题的一种情况是如果文件为缩短。我不确定当您阅读时会发生什么超越尽头。

最后:系统不会任意打开,并且修改文件。这是一个源文件;会是个白痴执行此操作的程序员,他应该得到他得到的。在没有案例您的未定义行为会破坏系统或其他行为人民文件。

也请注意,大多数编辑器都可以在私人副本上工作;当。。。的时候回信,他们这样做是通过重命名原件并创建一个新文件。在UNIX下,一旦您将文件打开到mmap它所有的都是Inode号。当编辑器重命名或删除文件,您仍然保留副本。这修改后的文件将获得新的Inode。你唯一必须担心是有人打开文件以进行更新,然后四处修改。文本上的程序不多文件,除了将其他数据附加到末尾。

因此,虽然正式存在一些风险,但我认为您不必担心。(如果您真的很偏执,可以关闭在mmap ED时写授权。如果有真的是一个敌人的代理人来拿到你的,他可以把它立即转回on。)