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