【问题标题】:Why is previously allocated memory inaccessable after a failed realloc?为什么重新分配失败后以前分配的内存无法访问?
【发布时间】:2014-01-21 18:10:22
【问题描述】:

我需要为一个进程分配所有可用内存,以实现对系统服务的测试。测试(除其他外)需要耗尽所有可用资源、尝试调用并检查特定结果。

为了做到这一点,我写了一个循环,重新分配一块内存,直到,realloc返回null,然后使用最后一个好的分配,然后切割最后一个成功数量和最后一个不成功数量之间的差异,直到不成功数量比上一次成功的数量大 1 个字节,保证所有可用内存都被消耗完。

我写的代码如下(还包括调试打印)

#include <stdio.h>
#include <malloc.h>
int main(void)
{
    char*       X;
    char*       lastgood = NULL;
    char*       toalloc = NULL;
    unsigned int    top = 1;    
    unsigned int    bottom = 1; 
    unsigned int    middle;     
    do              
    {               
        bottom = top;
        lastgood = toalloc;     
        top = bottom*2;                 
        printf("lastgood = %p\ntoalloc = %p\n", lastgood, toalloc); 
        if (lastgood != NULL)           
            printf("*lastgood = %i\n", *lastgood);      
        toalloc = realloc(toalloc, top);    
        printf("lastgood = %p\ntoalloc = %p\n", lastgood, toalloc); 
        if (toalloc == NULL && lastgood != NULL)        
            printf("*lastgood = %i\n", *lastgood);  //segfault happens here 
    }while(toalloc != NULL);                    
    do                          
    {                           
        if (toalloc != NULL) lastgood = toalloc;        
        else toalloc = lastgood;                
        middle = bottom+(top - bottom)/2;           
        toalloc = realloc(toalloc, middle);         
        if (toalloc == NULL) top = middle;          
        else bottom = middle;               
    }while(top - bottom > 1);               
    if (toalloc != NULL) lastgood = toalloc;                
        X = lastgood;
//make a call that attempts to get more memory
    free(X);
}

根据 realloc 的手册页,如果 realloc 返回 null,它不会破坏之前的地址。即便如此,此代码在 toalloc 从 realloc 接收到 NULL 后尝试打印 lastgood 时会导致段错误。为什么会发生这种情况,有没有更好的方法来获取确切数量的未分配内存?

我在 glibc 上运行它,在内核 3.11.x 的 ubuntu 上运行它

【问题讨论】:

  • 作为健全性检查,realloc 至少成功了一次,对吧?
  • 是的。它在最后几个周期产生以下输出: ii = 0x7f3be8806010 jj = 0x7f3b68805010 ii = 0x7f3b68805010 jj = 0x7f3b68805010 *ii = 0 ii = 0x7f3b68805010 jj = (nil) 分段错误(核心转储)
  • Linux 过度提交内存分配。要验证realloc 返回的内容实际上是可用,您需要,嗯,使用它。我不知道其他操作系统。
  • 我正在使用它。每个周期。根据我之前的评论,它实际上是在没有段错误的情况下成功使用的。段错误仅在 realloc 失败时发生。查看评论中的输出!
  • realloc()失败时top的值是多少?

标签: c++ c segmentation-fault realloc


【解决方案1】:

您没有检查top 的值是否溢出。这就是它的价值:

2
4
8
16
32
64
128
256
512
1024
2048
4096
8192
16384
32768
65536
131072
262144
524288
1048576
2097152
4194304
8388608
16777216
33554432
67108864
134217728
268435456
536870912
1073741824
2147483648
0

就在最后一个realloc() 之前,top 的新值再次为0(实际上是2^32,但它不适合32 位),这似乎导致内存块实际释放。


尝试分配最大的连续块不是一个好主意。用户进程看到的内存映射已经为共享库分配了一些块,以及当前进程的实际代码和数据。除非您想知道可以分配的最大连续内存块,否则最好的方法是在单个块中分配尽可能多的内存。当你到达那个位置时,用不同的指针做同样的事情,并继续这样做,直到你真的耗尽内存。考虑到在 64 位系统中,您不会在一个 malloc()/realloc() 中获得所有可用内存。正如我刚刚看到的,64 位系统中的malloc() 在一次调用中最多分配4GB 的内存,即使您可以发出多个mallocs() 并且仍然在每个调用上都成功。

在我几天前给出的答案中描述了在 32 位 Linux 系统中看到的用户进程内存映射的视觉效果: Is kernel space mapped into user space on Linux x86?

我想出了这个程序,它可以“吃掉”所有的内存:

#include <stdio.h>
#include <malloc.h>

typedef struct slist
{
  char *p;
  struct slist *next;
} TList;

int main(void)
{
  size_t nbytes;
  size_t totalbytes = 0;
  int i = 0;
  TList *list = NULL, *node;

  node = malloc (sizeof *node);
  while (node)
  {
    node->next = list;
    list = node;
    nbytes = -1; /* can I actually do this? */ 
    node->p  = malloc(nbytes);  
    while (nbytes && !node->p)
    {
      nbytes/=2;
      node->p = malloc(nbytes);
    }
    totalbytes += nbytes + sizeof *node;
    if (nbytes==0)
      break;
    i++;
    printf ("%8d", i);
  }
  printf ("\nBlocks allocated: %d. Memory used: %f GB\n", 
                 i, totalbytes/(1024*1048576.0));
  return 0;
}

在 32 位 Linux 系统中执行会产生这些值:

 1       2       3       4       5       6       7       8       9      10
11      12      13      14      15      16      17      18      19      20
21      22      23      24      25      26      27      28      29      30
31      32      33      34      35      36      37      38      39      40
41      42      43      44      45      46      47      48      49      50
51      52      53
Blocks allocated: 53. Memory used: 2.998220 GB

非常接近 32 位 Linux 系统的 3GB 限制。在 64 位 Linux 系统上,我已经达到了 300004GB 每个,并且还在计数。我真的不知道Linux是否可以分配那么多内存或者这是我的错误。根据this,最大虚拟地址空间为128TB(即327684GB块)


更新:事实上,确实如此。我已经在 64 位机器上运行该程序,在 110074 成功分配块之后,分配的内存总量为 131071.578884 GB 。每个malloc()每次操作都可以分配4 GB,但是当达到115256 GB时,它已经开始分配到2 GB,然后当达到123164 GB时,它开始分配到1GBmalloc()。进程不同步地趋向于131072 GB,但实际上它停止得更早一点,131071.578884 GB,因为这个进程、它的数据和共享库都使用了几 KB 的内存。

【讨论】:

  • 嗯,这回答了它失败的原因,但这给我带来了一个新问题:有没有更好的方法来从系统中获取确切数量的未分配内存?
  • 最后一点,引用 realloc 的手册页:if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).
  • @user2149140:您指的是哪个“未分配内存的确切数量”?您是指堆从操作系统分配的虚拟内存,但堆中仍然空闲(调用malloc() 时会增加),还是“虚拟地址空间大小 - 使用的总虚拟空间”(与堆无关或实际 RAM),或“安装的总 RAM - 当前正在使用的实际 RAM”,或....?
【解决方案2】:

我认为getrlimit() 是您新问题的答案。这是手册页http://linux.die.net/man/2/getrlimit

您可能对RLIMIT_STACK 感兴趣。请查看手册页。

【讨论】:

    【解决方案3】:

    第一部分替换为以下内容。

    char *p;
    size_t size = SIZE_MAX;//SIZE_MAX : #include <stdint.h>
    do{
        p = malloc(size);
        size >>= 1;
    }while(p==NULL);
    

    【讨论】:

      猜你喜欢
      • 2013-08-24
      • 2021-05-06
      • 1970-01-01
      • 2015-09-22
      • 1970-01-01
      • 2012-05-09
      • 2012-07-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多