为什么在重新分配失败后,以前分配的内存无法访问?

时间:2014-01-03 23:11:19

标签: c++ c segmentation-fault realloc

我需要为进程分配所有可用内存,以便实现系统服务的测试。测试(以及其他测试)需要耗尽所有可用资源,尝试呼叫以及检查特定结果。

为了做到这一点,我写了一个循环来重新分配一块内存,直到,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的联机帮助页,如果返回null,则realloc不会销毁先前的地址。即便如此,当toalloc从realloc接收NULL时,此代码在尝试打印lastgood时会导致段错误。为什么会发生这种情况,是否有更好的方法来获取未分配内存的确切数量?

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

的ubuntu上运行

3 个答案:

答案 0 :(得分:5)

您没有检查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是否可以分配那么多内存,或者这是我的错误。根据{{​​3}},最大虚拟地址空间为128TB(即32768 4GB


更新:事实上,确实如此。我已经在64位的盒子上运行了这个程序,并且在110074成功分配了块后,分配的内存总量为131071.578884 GB。每个malloc()每个操作都能分配4 GB,但到达115256 GB时,它已开始分配最多2 GB,然后达到123164 GB1GB 1}}已分配,它开始每malloc()分配131072 GB。这种进展在合理上倾向于131071.578884 GB,但它实际上会稍早停止,{{1}}因为进程,它的数据和共享库使用了几KB的内存。

enter image description here

答案 1 :(得分:1)

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

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

答案 2 :(得分:0)

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

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