linux 内存Map文件大小不断增加

6ioyuze2  于 2022-11-02  发布在  Linux
关注(0)|答案(2)|浏览(248)

我正在写一个程序来同时读和写一个文件。更具体地说,所有的写操作都是把新数据附加到文件的末尾,所有的读操作都是阅读文件的随机位置。
我正在考虑创建内存Map文件(使用mmap),以便通过append(模式aopen中)在写入时实现高效的读取。但是,我认为这不会起作用,因为内存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文件的大小。
m2xkgtsf

m2xkgtsf1#

内存Map文件大小不能更改
是的,它可以。只要使用ftruncate来增长文件。
很难改变 mapping 的大小,但这是分开的,你可以有多个部分Map,所以诀窍是把文件Map成离散的固定大小的段。
一般来说,最好不要要求始终Map整个文件,因为这样会限制您只能Map适合内存的文件。但是,如果您希望保持指向文件的随机指针,那么保持段的LRU缓存可能是不可能的。

ia2d9nvy

ia2d9nvy2#

经过几次实验,我找到了一种方法让它工作。
首先mmap文件大小为PROT_NONE且足够大。对于64位系统,文件大小可以为1L << 46(64 TB)。这不会消耗物理内存 *(至少在Linux上是这样)。它将为此进程消耗地址空间(虚拟内存)。

void* ptr = mmap(NULL, (1L << 40), PROT_NONE, MAP_SHARED, fd, 0);

然后,使用mprotect对文件长度内的内存部分给予读(和/或写)权限。注意,大小需要与页大小对齐(可通过sysconf(_SC_PAGESIZE)获得,通常为4096)。

mprotect(ptr, aligned_size, PROT_READ | PROT_WRITE);

但是,如果文件大小未与页面大小对齐,则阅读Map区域内(具有PROT_READ权限)但超出文件长度的部分将触发总线错误,如mmap手册中所述。
然后,您可以使用文件描述符fd或Map的内存来读写文件。请记住,在写入数据后,使用fsyncmsync来保存数据。具有PROT_READ权限的内存Map页应获得最新的文件内容(如果您写入它)**。具有mprotect的新Map页也将获得新更新的页。
根据应用程序的不同,您可能希望使用ftruncate使文件大小与系统页大小一致以获得最佳性能。您可能还希望将madviseMADV_SEQUENTIAL一起使用以提高阅读这些页时的性能。

  • mmap的手册中没有提到这种行为。但是,由于PROT_NONE意味着这些页面无论如何都是不可访问的,因此对于任何操作系统实现来说,根本不向其分配任何物理内存都是微不足道的。
    **这种在文件写入之前Map的内存区域在写入完成后更新的行为(fsyncmsync)在手册上也没有提到(或者至少我没有看到)。但至少在最近的Linux内核(4.x以上)上似乎是这样的。

相关问题