【问题标题】:mmap fails when length is larger than 4GB长度大于 4GB 时 mmap 失败
【发布时间】:2011-06-15 17:54:43
【问题描述】:

(正确的代码在“Update 5”中)

在本示例 C 代码中,我尝试将内存范围从 0x100000000 映射到 0x200000000:

#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 r-x--  test
0000000000600000       4       4       4 rw---  test
00007f1cc941e000    1640     280       0 r-x--  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 r-x--  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 r-x--    [ anon ]
ffffffffff600000       4       0       0 r-x--    [ anon ]
----------------  ------  ------  ------
total kB            4040     476      80
pmap -x 5618
5618:   ./test
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000000400000       4       4       0 r-x--  test
0000000000600000       4       4       4 rw---  test
00007f1cc941e000    1640     280       0 r-x--  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 r-x--  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 r-x--    [ anon ]
ffffffffff600000       4       0       0 r-x--    [ 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

除了 pid 之外,其他输出与“Update 3”相同(仍然失败)。

Update 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 给出了正确答案:在 mmap 中添加 MAP_NORESERVE 标志可以解决这个问题。原因的详细信息在 caf 的回答中。非常感谢咖啡馆,所有这些都给予了帮助!

【问题讨论】:

  • 阅读手册页:“失败 -1,并且设置了 errno”。它可能会或可能不会告诉您任何有用的信息,但您应该在要求其他人调查之前将其打印出来。
  • 为什么要自己指定基地址?
  • @Tony:谢谢!我打印了错误消息,上面写着“无法分配内存”。
  • @sarnold:我们想在汇编中编写一些代码来读/写特定范围内的内存。我知道这对于普通应用程序来说有点奇怪。
  • @马志强,啊哈!谢谢 :) 我想知道这是否是 Address Space Layout Randomization 在每次运行时为您提供不同的“空闲空间”?

标签: c linux gcc fedora glibc


【解决方案1】:

如果您实际上没有配置超过 8G 的交换空间,那么该大型映射可能会失败。

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

【讨论】:

  • 太好了,谢谢!添加此标志后,mmap 将成功。实际上我只有 2GB 内存 + 2GB 交换空间。
  • 您应该知道,MAP_NORESERVE 在配置为严格提交费用的系统上完全被忽略(没有过度提交)。
【解决方案2】:

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

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

如果 overcommit_memory 不是 0,那么每个地址空间分配都必须由物理内存(RAM + 交换空间)支持,如果 overcommit_memory 0,则内存被过度使用,即内核将愉快地分配地址空间,但只有将数据写入分配的地址空间时才会分配内存。然后内存不会分配给完整的保留地址空间,而只会分配给那些被触摸的页面。这有点像预订机票:航空公司出售的机票通常比航班上的座位多,预计并非所有预订的乘客都会真正出现。现在您可能想知道,如果所有程序都利用了全部空间会发生什么……那么一些令人讨厌的事情开始了:Linux Out Of Memory Killer 将对您的系统造成严重破坏,并且很可能会杀死您最需要的那些进程,因为这是一种神秘的启发式方法。

overcommit_ratio 告诉内核

  • 在过度使用模式下,物理内存可能被过度使用的比率,即可以分配的地址空间比物理内存多多少。

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

因此,过度使用模式可能只是系统之间的不同。

【讨论】:

  • Linux VM 记账实际上有三种模式:Heuristic overcommit (0)、Always overcommit (1) 和 Never overcommit (2)。
  • 非常感谢!在这两个系统上,overcommit_ratio 为 50,而 overcommit_memory 为 0。我只有 2GB 内存 + 2GB 交换空间。按照@caf的建议添加MAP_NORESERVE标志后,它可以映射8GB内存。
【解决方案3】:

刚刚在 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);

【讨论】:

  • @Maxim 谢谢!我在 mmap 调用前后添加了 system("pmap -x $$") 。我已经发布了更新的结果(更新 2)。我不确定我是否以正确的方式使用它。请帮我看看。
  • 哎呀,我真傻; $$ 获取 shell pid。 pmap 正在转储错误的地图。将 system("pmap -x $$") 替换为 char pid[10]; sprintf(pid, "%i", getpid()); execlp("pmap",pid, (char *)NULL);
  • @sarnold:我更改了代码并打印了新结果(更新 3)。我生成正确的命令并通过调用 system.您提供的代码似乎缺少“-x”参数。我认为根据 pmap 的输出没有使用范围(0x100000000 到 0x200000000)。
  • @Maxim Yegorushkin:我用过类似的(你的更好:更安全;)),但同样感谢。我还在“更新 3”中发布了输出。
【解决方案4】:

由于您尝试映射到特定地址,当您调用mmap 时,它将取决于您的进程的当前内存布局。满足请求的地址的策略取决于系统,linux 手册页说了一些“提示”。

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

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

【讨论】:

  • 感谢您的建议!我在 mmap 调用中将 addr 更改为 NULL,但它仍然失败并给出“无法分配内存”错误消息。
【解决方案5】:

也许您遇到了资源限制?尝试添加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 r-x--  zhiqiang
0000000000600000       0       4       4 r----  zhiqiang
0000000000601000       0       4       4 rw---  zhiqiang
00007f37b3c27000       0     260       0 r-x--  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 r-x--  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 r-x--    [ anon ]
ffffffffff600000       0       0       0 r-x--    [ anon ]
----------------  ------  ------  ------
total kB            3912     464      88
pmap -x 23819
23819:   ./zhiqiang
Address           Kbytes     RSS   Dirty Mode   Mapping
0000000000400000       0       4       0 r-x--  zhiqiang
0000000000600000       0       4       4 r----  zhiqiang
0000000000601000       0       4       4 rw---  zhiqiang   
0000000400000000       0       0       0 rw---    [ anon ]
00007f37b3c27000       0     260       0 r-x--  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 r-x--  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 r-x--    [ anon ]
ffffffffff600000       0       0       0 r-x--    [ 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.

【讨论】:

  • 看起来像:ENOMEM 没有可用内存,或者已经超出进程的最大映射数。
  • 我在调用 mmap 之前添加了这个命令。输出显示在“更新 4”中。它显示“最大内存大小”和“虚拟内存”的“无限”。
  • 非常感谢您的帮助!我也尝试过使用不同版本的 kernel/gcc/libc 的不同系统。仅在装有 Fedora 14(系统 1)的笔记本电脑上,此程序失败。
  • @Zhiqiang Ma,如果@datenwolf 没有正确的答案,我认为是时候向 Fedora 提交错误报告了 :)
  • caf's 提供了正确的解决方案。 mmap 尝试为 8GB 内存预留空间,而我的计算机没有。添加 MAP_NORESERVE 标志将避免保留空间。这不是 Fedora 的错误,而是我的代码的错误。但很高兴找到它。
猜你喜欢
  • 1970-01-01
  • 2013-01-11
  • 2015-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-07-20
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多