我正在写一个程序来同时读和写一个文件。更具体地说,所有的写操作都是把新数据附加到文件的末尾,所有的读操作都是阅读文件的随机位置。
我正在考虑创建内存Map文件(使用mmap
),以便通过append(模式a
在open
中)在写入时实现高效的读取。但是,我认为这不会起作用,因为内存Map文件的大小不能改变 *,除非我先munmap
,然后mmap
它。
虽然“munmap
然后再mmap
文件”可以工作,但它也有很多缺点。(或在每次读取之前),这会损害性能,munmap
之后的下一个mmap
调用返回的基址可能与前一个不同。由于我计划在存储指向该存储器Map文件的特定偏移量的指针的存储器数据结构,则可能非常不方便。
有没有更优雅更有效的方法来实现这一点?程序将主要运行在Linux上(但最好是能够移植到其他POSIX系统的解决方案)。我已经阅读了以下帖子,但似乎没有一个给予明确的答案。
How to portably extend a file accessed using mmap()
Can the OS automatically grow an mmap backed file?
Fast resize of a mmap file
我的直觉是使用mmap
来“保留”文件,使其大小足以容纳文件的增长,比如说几百GiB(在我的用例中这是一个非常合理的假设)。然后以某种方式在这个Map内存中反映文件大小的变化,而不使用munmap
使其无效。但是,我知道访问超出真实的文件边界的数据可能会导致总线错误,而且文档也不清楚文件大小的变化是否会被反映出来。
- 我不是100%肯定这一点,但我找不到任何来源优雅地改变内存Map文件的大小。
2条答案
按热度按时间m2xkgtsf1#
内存Map文件大小不能更改
是的,它可以。只要使用
ftruncate
来增长文件。很难改变 mapping 的大小,但这是分开的,你可以有多个部分Map,所以诀窍是把文件Map成离散的固定大小的段。
一般来说,最好不要要求始终Map整个文件,因为这样会限制您只能Map适合内存的文件。但是,如果您希望保持指向文件的随机指针,那么保持段的LRU缓存可能是不可能的。
ia2d9nvy2#
经过几次实验,我找到了一种方法让它工作。
首先
mmap
文件大小为PROT_NONE
且足够大。对于64位系统,文件大小可以为1L << 46
(64 TB)。这不会消耗物理内存 *(至少在Linux上是这样)。它将为此进程消耗地址空间(虚拟内存)。然后,使用
mprotect
对文件长度内的内存部分给予读(和/或写)权限。注意,大小需要与页大小对齐(可通过sysconf(_SC_PAGESIZE)
获得,通常为4096)。但是,如果文件大小未与页面大小对齐,则阅读Map区域内(具有
PROT_READ
权限)但超出文件长度的部分将触发总线错误,如mmap
手册中所述。然后,您可以使用文件描述符
fd
或Map的内存来读写文件。请记住,在写入数据后,使用fsync
或msync
来保存数据。具有PROT_READ
权限的内存Map页应获得最新的文件内容(如果您写入它)**。具有mprotect
的新Map页也将获得新更新的页。根据应用程序的不同,您可能希望使用
ftruncate
使文件大小与系统页大小一致以获得最佳性能。您可能还希望将madvise
与MADV_SEQUENTIAL
一起使用以提高阅读这些页时的性能。mmap
的手册中没有提到这种行为。但是,由于PROT_NONE
意味着这些页面无论如何都是不可访问的,因此对于任何操作系统实现来说,根本不向其分配任何物理内存都是微不足道的。**这种在文件写入之前Map的内存区域在写入完成后更新的行为(
fsync
或msync
)在手册上也没有提到(或者至少我没有看到)。但至少在最近的Linux内核(4.x以上)上似乎是这样的。