当长度大于4GB时,mmap将失败

(正确的代码在'Update 5'中)

我试图映射从0x100000000到0x200000000在这个例子C代码的内存范围:

#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <sys/mman.h> int main(void) { uint64_t* rr_addr = 0; uint64_t i = 17179869184; printf("\nsizeof(size_t): %llu\n", sizeof(size_t)); printf("(uint64_t)0x100000000: %llx\n", (uint64_t)0x100000000); printf("1L << 33: %llx\n", 1L << 33); rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); printf("rr_addr: %p, %llu \n", rr_addr, rr_addr); if (rr_addr == MAP_FAILED) { perror("mmap error"); } return 0; } 

在不同的系统上(Linux,gcc),我得到了不同的结果:

结果1:

 sizeof(size_t): 8 (uint64_t)0x100000000: 100000000 1L << 33: 200000000 rr_addr: 0xffffffffffffffff, 18446744073709551615 mmap error: Cannot allocate memory 

系统信息(Fedora 14):

 Linux localhost.localdomain 2.6.35.10-74.fc14.x86_64 #1 SMP Thu Dec 23 16:04:50 UTC 2010 x86_64 x86_64 x86_64 GNU/Linux gcc (GCC) 4.5.1 20100924 (Red Hat 4.5.1-4) glibc: 2.12.90-21 

结果2:

 sizeof(size_t): 8 (uint64_t)0x100000000: 100000000 1L << 33: 200000000 rr_addr: 0x400000000, 17179869184 

系统信息(Fedora 12):

 Linux wiles 2.6.32.13 #2 SMP Fri Sep 10 01:29:43 HKT 2010 x86_64 x86_64 x86_64 GNU/Linux gcc (GCC) 4.4.4 20100630 (Red Hat 4.4.4-10) glibc verison: 2.11.2-1 

我期待“结果2”。 也许我的代码有问题。

请帮我一下

更新1 :如果mmap失败,errno将被打印出来。

更新3 :将mmap调用更改为以下行之后:

 char *cmd[20]; sprintf(cmd, "pmap -x %i", getpid()); printf("%s\n", cmd); system(cmd); rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); printf("%s\n", cmd); system(cmd); 

结果:

 sizeof(size_t): 8 (uint64_t)0x100000000: 100000000 1L << 33: 200000000 pmap -x 5618 5618: ./test Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 rx-- test 0000000000600000 4 4 4 rw--- test 00007f1cc941e000 1640 280 0 rx-- libc-2.12.90.so 00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so 00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so 00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so 00007f1cc97bc000 24 16 16 rw--- [ anon ] 00007f1cc97c2000 132 108 0 rx-- ld-2.12.90.so 00007f1cc99c6000 12 12 12 rw--- [ anon ] 00007f1cc99e0000 8 8 8 rw--- [ anon ] 00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so 00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so 00007f1cc99e4000 4 4 4 rw--- [ anon ] 00007fffa0da8000 132 8 8 rw--- [ stack ] 00007fffa0dff000 4 4 0 rx-- [ anon ] ffffffffff600000 4 0 0 rx-- [ anon ] ---------------- ------ ------ ------ total kB 4040 476 80 pmap -x 5618 5618: ./test Address Kbytes RSS Dirty Mode Mapping 0000000000400000 4 4 0 rx-- test 0000000000600000 4 4 4 rw--- test 00007f1cc941e000 1640 280 0 rx-- libc-2.12.90.so 00007f1cc95b8000 2044 0 0 ----- libc-2.12.90.so 00007f1cc97b7000 16 16 16 r---- libc-2.12.90.so 00007f1cc97bb000 4 4 4 rw--- libc-2.12.90.so 00007f1cc97bc000 24 16 16 rw--- [ anon ] 00007f1cc97c2000 132 108 0 rx-- ld-2.12.90.so 00007f1cc99c6000 12 12 12 rw--- [ anon ] 00007f1cc99e0000 8 8 8 rw--- [ anon ] 00007f1cc99e2000 4 4 4 r---- ld-2.12.90.so 00007f1cc99e3000 4 4 4 rw--- ld-2.12.90.so 00007f1cc99e4000 4 4 4 rw--- [ anon ] 00007fffa0da8000 132 8 8 rw--- [ stack ] 00007fffa0dff000 4 4 0 rx-- [ anon ] ffffffffff600000 4 0 0 rx-- [ anon ] ---------------- ------ ------ ------ total kB 4040 476 80 rr_addr: 0xffffffffffffffff, 18446744073709551615 mmap error: Cannot allocate memory 

更新4 :添加“system(”ulimit -m -v“);” 就在调用mmap之前:ulimit的输出是:

 max memory size (kbytes, -m) unlimited virtual memory (kbytes, -v) unlimited 

其他输出与“更新3”(仍然失败)相同,除了PID。

更新5 :在两个系统上工作的更新的代码:

 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <sys/mman.h> int main(void) { uint64_t* rr_addr = 0; uint64_t i = 17179869184; uint64_t len = 0; char cmd[20]; printf("\nsizeof(size_t): %llu\n", sizeof(size_t)); len = (1UL << 32); printf("len: %llx\n", len); snprintf(cmd, sizeof cmd, "pmap -x %i", getpid()); printf("%s\n", cmd); system(cmd); system("ulimit -m -v"); rr_addr = mmap((void*)i, len, PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE|MAP_NORESERVE, -1, 0); printf("%s\n", cmd); system(cmd); printf("rr_addr: %p, %llu \n", rr_addr, rr_addr); if (rr_addr == MAP_FAILED) { perror("mmap error"); } return 0; } 

@caf给出了正确的答案:将MAP_NORESERVE标志添加到mmap解决了这个问题。 详细的原因在咖啡的答案。 感谢很多咖啡,所有这些给予善意的帮助!

如果实际上没有配置大于8G的交换,那么这个大的映射可能会失败。

您可以将MAP_NORESERVE标志添加到mmap()以告诉它不要为MAP_NORESERVE映射保留任何交换空间。

有多少物理内存? Linux有两种不同的地址空间分配模式:写入时的内存分配(即过度使用模式)或地址空间分配时的内存分配。 您可以通过阅读procfs中的两个文件来检查:

 cat /proc/sys/vm/overcommit_memory cat /proc/sys/vm/overcommit_ratio 

如果overcommit_memory 不是 0,那么每个地址空间分配必须由物理内存(RAM +交换空间)支持,如果overcommit_memory 0,则内存被过度占用,即内核将愉快地分配地址空间,但内存将是唯一的如果数据写入分配的地址空间则分配。 然后内存不会被分配给完整的保留地址空间,而只能分配给那些被触摸的页面。 这就像预订机票一样:航空公司通常会出售比机上有座位更多的机票,预计并非所有预订的乘客都会出现。 现在你可能会想,如果所有的程序都利用了整个空间会发生什么……那么一些讨厌的事情就会发生:Linux内存不足杀手会对你的系统造成严重破坏,很可能会导致你最需要的进程被终止这是神秘的启发式。

overcommit_ratio告诉内核

  • 在过度使用模式下,物理内存可能被过度占用,即有多少地址空间可能被分配出来,而不是物理内存。

  • 在非过度使用模式下需要保留多少内存

所以也许过度使用模式只是不同的系统之间。

刚刚在Fedora 13上运行你的代码,并产生结果2。

当mmap()返回MAP_FAILED(-1)时,检查errno。 您也可以在mmap调用之前和之后粘贴以下行,以查看在4GB区域的进程的虚拟地址空间中是否有空间:

 system("pmap -x $$"); 

更新:上面实际上是打印子进程的地图。 正确的代码:

 char buf[0x100]; snprintf(buf, sizeof buf, "pmap -x %u", (unsigned)getpid()); system(buf); 

由于您尝试映射到特定的地址,因此在调用mmap时,将取决于当前的进程内存布局。 请求的地址所遵循的策略是依赖于系统的,linux手册页中提到了一些“提示”。

因此,也许在第一种情况下,在进程的虚拟地址空间中没有足够的空间来满足请求,因为在该范围内已经有另一个映射。

检查这是否与此相关的一个好主意是在不提供addr提示时检查是否成功。

也许你遇到了资源限制? 尝试添加system("ulimit -m -v"); 打印出可以分配的内存和地址空间量。

编辑 :好吧,我没有想法。 抱歉。 清理代码中的错误和警告之后,我有这个来源:

 #include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <sys/mman.h> int main(void) { uint64_t* rr_addr = 0; uint64_t i = 17179869184; printf("\nsizeof(size_t): %lu\n", sizeof(size_t)); printf("(uint64_t)0x100000000: %lx\n", (uint64_t)0x100000000); printf("1L << 33: %lx\n", 1L << 33); char cmd[20]; sprintf(cmd, "pmap -x %i", getpid()); printf("%s\n", cmd); system(cmd); rr_addr = mmap((void*)i, (1UL << 33), PROT_READ|PROT_WRITE, MAP_ANON|MAP_PRIVATE, -1, 0); printf("%s\n", cmd); system(cmd); printf("rr_addr: %p, %lu \n", rr_addr, rr_addr); if (rr_addr == MAP_FAILED) { perror("mmap error"); } return 0; } 

和这个输出:

 sizeof(size_t): 8 (uint64_t)0x100000000: 100000000 1L << 33: 200000000 pmap -x 23819 23819: ./zhiqiang Address Kbytes RSS Dirty Mode Mapping 0000000000400000 0 4 0 rx-- zhiqiang 0000000000600000 0 4 4 r---- zhiqiang 0000000000601000 0 4 4 rw--- zhiqiang 00007f37b3c27000 0 260 0 rx-- libc-2.12.1.so 00007f37b3da1000 0 0 0 ----- libc-2.12.1.so 00007f37b3fa0000 0 16 16 r---- libc-2.12.1.so 00007f37b3fa4000 0 4 4 rw--- libc-2.12.1.so 00007f37b3fa5000 0 12 12 rw--- [ anon ] 00007f37b3faa000 0 108 0 rx-- ld-2.12.1.so 00007f37b41aa000 0 12 12 rw--- [ anon ] 00007f37b41c7000 0 12 12 rw--- [ anon ] 00007f37b41ca000 0 4 4 r---- ld-2.12.1.so 00007f37b41cb000 0 4 4 rw--- ld-2.12.1.so 00007f37b41cc000 0 4 4 rw--- [ anon ] 00007fff70cf8000 0 12 12 rw--- [ stack ] 00007fff70dff000 0 4 0 rx-- [ anon ] ffffffffff600000 0 0 0 rx-- [ anon ] ---------------- ------ ------ ------ total kB 3912 464 88 pmap -x 23819 23819: ./zhiqiang Address Kbytes RSS Dirty Mode Mapping 0000000000400000 0 4 0 rx-- zhiqiang 0000000000600000 0 4 4 r---- zhiqiang 0000000000601000 0 4 4 rw--- zhiqiang 0000000400000000 0 0 0 rw--- [ anon ] 00007f37b3c27000 0 260 0 rx-- libc-2.12.1.so 00007f37b3da1000 0 0 0 ----- libc-2.12.1.so 00007f37b3fa0000 0 16 16 r---- libc-2.12.1.so 00007f37b3fa4000 0 4 4 rw--- libc-2.12.1.so 00007f37b3fa5000 0 12 12 rw--- [ anon ] 00007f37b3faa000 0 108 0 rx-- ld-2.12.1.so 00007f37b41aa000 0 12 12 rw--- [ anon ] 00007f37b41c7000 0 12 12 rw--- [ anon ] 00007f37b41ca000 0 4 4 r---- ld-2.12.1.so 00007f37b41cb000 0 4 4 rw--- ld-2.12.1.so 00007f37b41cc000 0 4 4 rw--- [ anon ] 00007fff70cf8000 0 12 12 rw--- [ stack ] 00007fff70dff000 0 4 0 rx-- [ anon ] ffffffffff600000 0 0 0 rx-- [ anon ] ---------------- ------ ------ ------ total kB 8392520 464 88 rr_addr: 0x400000000, 17179869184 

和我的系统的细节:

 Linux haig 2.6.35-24-generic #42-Ubuntu SMP Thu Dec 2 02:41:37 UTC 2010 x86_64 GNU/Linux gcc version 4.4.5 (Ubuntu/Linaro 4.4.4-14ubuntu5) GNU C Library (Ubuntu EGLIBC 2.12.1-0ubuntu10.1) stable release version 2.12.1, by Roland McGrath et al.