【问题标题】:malloc under linux, implicit limitlinux下malloc,隐式限制
【发布时间】:2012-12-12 13:51:38
【问题描述】:

很抱歉,如果标题没有达到应有的描述性,这个问题很难用几句话来形容。我试图通过 malloc'ing 找出我有多少可用内存,如果可行,则写入该段。在某些系统(x86_64 上的所有 linux)上,我在写入第 2049 个 mib 时看到了段错误。代码是:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/mman.h>

int main (int argc, char **argv) {
    void *addr;
    int megasize = 2100
    /// allocate the memory via mmap same result
    //addr = mmap ((void *) 0, (size_t) megasize << 20, PROT_READ | PROT_WRITE,
    //                                                      MAP_PRIVATE | MAP_ANONYMOUS, (int) -1, (off_t) 0);
    addr = malloc(megasize << 20);
    if (addr == MAP_FAILED) {
            fprintf (stderr, "alloc of %d megabytes failed %s\n", megasize,
            strerror (errno));
            exit (1);
    };
    printf ("got %d megabytes at %p\n", megasize, addr);
    {
            int i;
            char *p = addr;
            printf("touching the %d Mb memory:\n", megasize);
            for (i = 0; i < megasize; i++) {
                    p[i << 20] = 0;
                    putchar('.');
                    if (i%64==63) // commenting out this line i see that it really is the 2049th mb
                            printf(" #%d\n", i);
                    fflush(stdout);
            };
            putchar('\n');
    };
    /// free the memory
    munmap (addr, (size_t) megasize << 20);
    return 0;
} 

它在某些系统上可靠地发生段错误,而在其他系统上则可以正常工作。阅读失败的系统的日志告诉我它不是 oom 杀手。我可以选择一些会导致 malloc 失败的 megasize 值,但这些值更大。 对于大于 2gib 且小于 malloc 为这些系统返回 -1 的限制的任何大小,段错误都会可靠地发生。

我相信我达到了一个 malloc 没有观察到的限制,我无法弄清楚它是什么。我尝试通过 getrlimit 读出一些似乎相关的限制,例如 RLIMIT_AS 和 RLIMIT_DATA 但这些限制要大得多。

这是我的 valgrindlog 的相关部分

==29126== Warning: set address range perms: large range [0x39436000, 0xbc836000) (defined)
==29126== Invalid write of size 1
==29126==    at 0x400AAD: main (in /home/max/source/scratch/memorytest)
==29126==  Address 0xffffffffb9436000 is not stack'd, malloc'd or (recently) free'd

谁能告诉我问题出在哪里?

【问题讨论】:

  • 启用或不启用过量使用? (这是默认的)
  • sizeof(int) == 4 我相信,并且int 已签名...尝试超过 2G 的值将导致负偏移。
  • @R.. 技术上是正确的,但与乘法不同的是,GCC 目前实际上支持有符号左移到符号位作为扩展,并且可以相当安全地猜测 GCC 是所使用的编译器。是的,就标准 C 而言,这是未定义的行为,但#include &lt;sys/mman.h&gt; 也是如此。
  • @Mat:我不这么认为。我几乎可以肯定我已经看到 for (i=1; i&gt;0; i&lt;&lt;=1); 形式的代码在某些版本的 GCC 上被编译为无限循环。
  • (@R..:我没有争论,你是对的。)

标签: c linux malloc


【解决方案1】:

通过int i 计数时会出现溢出,因为int 在这里是 4 个字节宽:

p[i << 20] = ...

改变

int i;

成为

size_t i;

size_t 是寻址内存时的首选类型。

【讨论】:

    【解决方案2】:

    32 位 int 无法存储值 2049 mb。您正在通过有符号整数溢出调用未定义的行为,并且碰巧得到一个负数。在大多数 32 位机器上,当添加到一个指针时,该指针会返回并最终为您提供所需的地址,意外。在 64 位机器上,这会为您提供大约 2047 mb 的地址低于您的内存块的开始(或环绕到 64 位内存空间的顶部)。

    使用正确的类型。这里,i 的类型应该是 size_t

    【讨论】:

      猜你喜欢
      • 2019-05-08
      • 1970-01-01
      • 2013-09-03
      • 2010-09-07
      • 2021-09-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多