为什么在一个1TB的稀疏文件上,mmap()会因ENOMEM而失败?

9
我一直在使用openSUSE 11.2 x86_64处理大型稀疏文件。当我尝试对一个1TB的稀疏文件进行mmap()时,会出现ENOMEM错误。我本以为64位地址空间足以映射到1TB,但似乎不是这样。进一步实验表明,1GB的文件可以正常工作,但2GB的文件(以及任何更大的文件)都会失败。我猜测可能有某个设置需要调整,但广泛搜索没有发现任何信息。
以下是一些示例代码,显示了问题-有什么线索吗?
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <unistd.h>

int main(int argc, char *argv[]) {
    char * filename = argv[1];
    int fd;
    off_t size = 1UL << 40; // 30 == 1GB, 40 == 1TB

    fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
    ftruncate(fd, size);
    printf("Created %ld byte sparse file\n", size);

    char * buffer = (char *)mmap(NULL, (size_t)size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if ( buffer == MAP_FAILED ) {
        perror("mmap");
        exit(1);
    }
    printf("Done mmap - returned 0x0%lx\n", (unsigned long)buffer);

    strcpy( buffer, "cafebabe" );
    printf("Wrote to start\n");

    strcpy( buffer + (size - 9), "deadbeef" );
    printf("Wrote to end\n");

    if ( munmap(buffer, (size_t)size) < 0 ) {
        perror("munmap");
        exit(1);
    }
    close(fd);

    return 0;
}

值得一提的是,您的程序在我的计算机上可以工作,最高测试到了256GB(1 << 38),而超过这个大小会返回 EINVAL。这是在RHEL4(内核2.6.9-42.0.3.ELsmp)上进行的测试。 - caf
5
“ulimit -a” 命令的输出是什么? - bmargulies
谢谢,bmargulies - 就是这个问题。 ulimit -a 报告虚拟内存为1804800 kbytes(略大于1.7GB)。 ulimit -v 1610612736(1.5TB)允许我映射我的1TB稀疏文件。我会回答自己的问题,这样我就可以“关闭”它... - metadaddy
3个回答

14

问题在于每个进程的虚拟内存限制只设置为了1.7GB。ulimit -v 1610612736将其设置为1.5TB,我的mmap()调用成功了。感谢bmargulies提示我尝试使用ulimit -a!


2
而且,显然,我可以在/etc/profile中设置我的期望值(可以是“无限制”),以使其持久化。 - metadaddy

2

有没有针对每个用户的配额,限制用户进程可用的内存量?


是的 - 我尝试了bmargulies的建议,尝试使用ulimit -a,并指出“虚拟内存”进程限制是罪魁祸首 - 请参见下面的答案... - metadaddy

1

我的猜测是内核在分配所需内存以跟上此内存映射方面遇到了困难。我不知道 Linux 内核如何跟踪交换出的页面(我认为大多数文件大部分时间都处于交换出状态),但它可能最终需要一个表中文件占据的每个内存页面的条目。由于该文件可能会被多个进程进行内存映射,因此内核必须从进程的角度跟上映射情况,这将映射到另一个视角,然后映射到辅助存储器中(并包括设备和位置字段)。

这将适合您的可寻址空间,但可能不适合(至少不是连续地)物理内存中。

如果有人对 Linux 如何实现此功能了解更多,我很感兴趣听听。


4
Linux在页面实际被访问之前不会创建PTE(页表项)。当您创建映射时,它所做的只是创建一个单独的VMA(虚拟内存区域)结构,该结构基本上包含了mmap()中的信息。 - caf

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接