我一直在研究一个项目的内存映射文件,并且会感谢之前使用它们或决定不使用它们的人的想法,以及为什么?
特别是,我按照重要性顺序关注以下内容:
答案 0 :(得分:49)
我认为优势在于您减少了传统阅读文件方法所需的数据复制量。
如果您的应用程序可以在内存映射文件中“就地”使用该数据,则它可以在不复制的情况下进入;如果您使用系统调用(例如Linux的pread()),则通常涉及内核将数据从其自己的缓冲区复制到用户空间。这种额外的复制不仅需要时间,而且还可以通过访问这些额外的数据副本来降低CPU缓存的有效性。
如果实际上必须从光盘读取数据(如在物理I / O中),那么操作系统仍然必须读入它们,页面错误可能没有比系统调用更好的性能,但如果他们不这样做(即已经在OS缓存中),理论上表现应该要好得多。
缺点是,内存映射文件没有异步接口 - 如果您尝试访问未映射的页面,它会生成页面错误,然后让线程等待I / O.
内存映射文件的明显缺点是在32位操作系统上 - 您可以轻松地耗尽地址空间。
答案 1 :(得分:44)
我在用户输入时使用了内存映射文件来实现“自动完成”功能。我有超过100万个产品部件号存储在一个索引文件中。该文件有一些典型的标题信息,但文件的大部分是按键字段排序的固定大小记录的巨大数组。
在运行时,文件被内存映射,强制转换为C
- 样式struct
数组,我们进行二进制搜索以查找用户类型的匹配部件号。实际上只从磁盘读取文件的几个内存页面 - 在二进制搜索期间命中哪个页面。
答案 2 :(得分:21)
内存映射文件可用于替换读/写访问,或支持并发共享。当你将它们用于一种机制时,你也会得到另一种机制。
不是在文件中寻找,写作和阅读,而是将其映射到内存中,只需访问您期望它们的位。
这可以非常方便,并且取决于虚拟内存接口可以提高性能。性能提升可能是因为操作系统现在可以管理这个以前的“文件I / O”以及所有其他程序化内存访问,并且(理论上)可以利用它已经用于支持的分页算法等等虚拟内存,用于程序的 rest 。但是,它确实取决于底层虚拟内存系统的质量。我听过的轶事说Solaris和* BSD虚拟内存系统可能比Linux的VM系统表现出更好的性能改进 - 但我没有经验数据来支持这一点。 YMMV。
当您考虑通过映射内存使用相同“文件”的多个进程的可能性时,并发性就出现了。在读/写模型中,如果两个进程写入文件的同一区域,您可以非常放心,其中一个进程的数据将到达文件中,覆盖其他进程的数据。你会得到一个或另一个 - 但不是一些奇怪的混合。我不得不承认我不确定这是否是任何标准规定的行为,但它是你可以非常依赖的东西。 (这实际上是一个很好的后续问题!)
相比之下,在映射的世界中,想象两个过程都是“写作”。他们这样做是通过“内存存储”来实现的,这会导致O / S将数据分页到磁盘 - 最终。但与此同时,可能会发生重叠写入。
这是一个例子。假设我有两个进程都在偏移1024处写入8个字节。进程1正在写入'11111111'而进程2正在写'22222222'。如果他们使用文件I / O,那么你可以想象,在O / S的深处,有一个满1秒的缓冲区,以及一个满2秒的缓冲区,两个都朝向磁盘上的同一个地方。其中一个是先到达那里,另一个是第二个。在这种情况下,第二个获胜。 然而,如果我使用内存映射文件方法,进程1将进入4字节的内存存储,然后是另一个4字节的内存存储(让我们假设不是最大内存商店大小)。流程2将做同样的事情。根据进程运行的时间,您可以看到以下任何内容:
11111111
22222222
11112222
22221111
对此的解决方案是使用显式互斥 - 这在任何情况下都可能是一个好主意。无论如何,你有点依赖于操作系统在读/写文件I / O情况下做“正确的事情”。
分类互斥原语是互斥锁。对于内存映射文件,我建议您查看内存映射的互斥锁,可以使用(例如)pthread_mutex_init()。
使用一个问题进行编辑:当您使用映射文件时,有一种诱惑就是在文件中嵌入指向文件中数据的指针(想想存储在映射文件中的链表)。您不希望这样做,因为文件可能在不同的时间或不同的进程中映射到不同的绝对地址。而是在映射文件中使用偏移量。
答案 3 :(得分:1)
并发将成为一个问题。 随机访问更容易 表现很好。 便于使用。不太好。 便携性 - 不那么热。
很久以前我在Sun系统上使用它们,这些都是我的想法。