我正在运行以下(最小复制)代码:
#include <stdio.h>
#include <sys/mman.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
void main() {
int fd = open("file.data", O_RDONLY);
void* ptr = mmap(0, (size_t)240 * 1024 * 1024 * 1024, PROT_READ, MAP_SHARED, fd, 0);
printf("Result = %p\n", ptr);
printf("Errno = %d\n", errno);
}
它输出(用gcc test.c && ./a.out
编译和运行):
Result = 0xffffffffffffffff
Errno = 9
file.data
是一个243GiB的文件:
$ stat file.data
File: file.data
Size: 260165023654 Blocks: 508135088 IO Block: 4096 regular file
Device: 801h/2049d Inode: 6815790 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1001/ user) Gid: ( 1001/ user)
Access: 2023-05-08 09:22:07.314477587 -0400
Modify: 2023-06-16 07:53:12.275187040 -0400
Change: 2023-06-16 07:53:12.275187040 -0400
Birth: -
其他配置(debian stretch,Linux 5.2.21):
$ sysctl vm.overcommit_memory
vm.overcommit_memory = 1
$ ulimit -a
core file size (blocks, -c) 0
data seg size (kbytes, -d) unlimited
scheduling priority (-e) 0
file size (blocks, -f) unlimited
pending signals (-i) 768178
max locked memory (kbytes, -l) unlimited
max memory size (kbytes, -m) unlimited
open files (-n) 1024
pipe size (512 bytes, -p) 8
POSIX message queues (bytes, -q) 819200
real-time priority (-r) 0
stack size (kbytes, -s) 8192
cpu time (seconds, -t) unlimited
max user processes (-u) 768178
virtual memory (kbytes, -v) unlimited
file locks (-x) unlimited
$ free -m
total used free shared buff/cache available
Mem: 192105 671 189213 9 2220 190314
Swap: 0 0 0
我已经遵循的建议:
- The mapping is not MAP_PRIVATE, and not PROT_WRITE
ulimit -v
is set to unlimitedvm.overcommit_memory=1
根据我的理解,我应该能够mmap
这个文件。我将它Map为只读,因此内核可以在需要时自由地将其全部交换回磁盘。应该有足够的连续内存,因为这是一个64位系统。
如何使mmap
调用工作?
编辑:/proc/*/maps
程序输出:
2aaaaaa000-2aaaaab000 r-xp 00000000 08:01 6815754 /home/<username>/a.out
2aaacaa000-2aaacab000 r--p 00000000 08:01 6815754 /home/<username>/a.out
2aaacab000-2aaacac000 rw-p 00001000 08:01 6815754 /home/<username>/a.out
3ff7a3a000-3ff7bcf000 r-xp 00000000 08:01 5767499 /lib/x86_64-linux-gnu/libc-2.24.so
3ff7bcf000-3ff7dcf000 ---p 00195000 08:01 5767499 /lib/x86_64-linux-gnu/libc-2.24.so
3ff7dcf000-3ff7dd3000 r--p 00195000 08:01 5767499 /lib/x86_64-linux-gnu/libc-2.24.so
3ff7dd3000-3ff7dd5000 rw-p 00199000 08:01 5767499 /lib/x86_64-linux-gnu/libc-2.24.so
3ff7dd5000-3ff7dd9000 rw-p 00000000 00:00 0
3ff7dd9000-3ff7dfc000 r-xp 00000000 08:01 5767249 /lib/x86_64-linux-gnu/ld-2.24.so
3ff7fe8000-3ff7fea000 rw-p 00000000 00:00 0
3ff7ff8000-3ff7ffb000 r--p 00000000 00:00 0 [vvar]
3ff7ffb000-3ff7ffc000 r-xp 00000000 00:00 0 [vdso]
3ff7ffc000-3ff7ffd000 r--p 00023000 08:01 5767249 /lib/x86_64-linux-gnu/ld-2.24.so
3ff7ffd000-3ff7ffe000 rw-p 00024000 08:01 5767249 /lib/x86_64-linux-gnu/ld-2.24.so
3ff7ffe000-3ff7fff000 rw-p 00000000 00:00 0
3ffffde000-3ffffff000 rw-p 00000000 00:00 0 [stack]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0 [vsyscall]
2条答案
按热度按时间i7uaboj41#
我无法复制你的问题
我修改了您的程序,添加了一个选项来生成示例/测试文件:
1.它可以 * 只是 * 做一个
truncate
来创建一个大文件。这只需要几分之一秒。1.然后,它可以用真实的数据填充它。在我的系统上创建一个243 GB的文件大约需要10分钟。
1.结果在任一模式下都是相同的。所以,IMO,快速模式是足够的(即,锉刀有孔)。换句话说,任何人都可以在几秒钟内在他们的系统上运行该程序。
我尝试了我能想到的所有组合和其他选择。在任何情况下,我都不能复制。下面是我的系统和你的系统的比较。
在阅读下面的内容后,如果你能想到任何其他的想法,我很乐意在我的系统上尝试一下,重现你的失败。
以下是修改后的程序:
下面是我的配置:
细微差别:
1.你有192 GB的RAM。但是,我只有12 GB的RAM。这种差异应该对你有利。但事实并非如此。该程序在我的系统上工作,该系统的RAM量不到1/10。
1.我有一个128 GB的交换磁盘。但是,我在执行
swapoff -a
后重新运行了该程序,以禁用所有交换磁盘。程序运行中没有差异。vm.overcommit_memory
为0。但是,我将其设置为1,并且程序操作中没有 * 任何 * 差异。1.在我的
vm.mmap_min_addr
上是65536(参见下面的TASK_SIZE
)1.我的电脑系统已经用了十多年了。
1.我运行的是一个更老的内核版本。
在测试的时候,我有:
1.几个
gnome-terminal
窗口firefox
,页面位于SO上thunderbird
1.一些背景shell程序[我自己设计的]。
由于我的内存小得多,我不得不质疑 neo-jgrec 的答案:
在x86(64位)系统上,
TASK_SIZE
可以是:1.正常系统:
1ul << 47
131,072 GB(128 TB)1.启用5级分页:
1ul << 56
67,108,864 GB(65,536 TB)即使使用更小的地址值,我们也显然不会超过
TASK_SIZE
我在过去对许多100+GB的文件做过
mmap
,没有问题。例如,看看我的答案:read line by line in the most efficient way platform specific下面是文件的状态:
下面是程序输出:
d7v8vwbk2#
您遇到的问题来自这样一个事实,即即使您的系统是64位系统,内核仍然有寻址限制,这取决于您的系统架构。
默认情况下,Linux将一半的可寻址内存空间分配给内核,另一半分配给用户。因此,对于一个64位系统,这将是2^63字节的内核和相同数量的用户空间。
但是,内核并没有使用整个空间。内核使用一个可寻址内存范围进行内存Map,即从
mmap_min_addr
到TASK_SIZE
。TASK_SIZE
通常在内核中设置为某个值,具体取决于系统的体系结构,该值可能小于最大可寻址空间。您的mmap请求可能会失败,因为它试图分配比系统的
TASK_SIZE
更多的内存。如果您尝试一次mmap 240GiB,它可能会超过系统上的TASK_SIZE
。一种解决方案是在循环中Map文件的较小块,直到Map完整个文件。下面是一个如何做到这一点的例子:
这段代码以64GiB为单位读取文件。最好根据您的具体情况调整块大小。您应该始终检查系统调用的返回值以处理任何错误。错误消息将更具描述性和信息性。
记住,在处理完文件的一个部分之后,在进入下一个部分之前,调用
munmap()
。