Почему ранее выделенная память недоступна после сбоя перераспределения?

0

Мне нужно выделить всю память, доступную для процесса, чтобы выполнить проверку системной службы. Тест (среди прочего) требует исчерпания всех доступных ресурсов, попыток вызова и проверки конкретного результата.

Чтобы сделать это, я написал цикл, который перераспределяет блок памяти до тех пор, пока 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 manpage, realloc не уничтожает предыдущий адрес, если он возвращает null. Тем не менее, этот код приводит к segfault, когда он пытается распечатать lastgood после того, как toalloc получает NULL из realloc. Почему это происходит, и есть ли лучший способ просто получить точное количество нераспределенной памяти?

Я запускаю его на glibc, на ubuntu с ядром 3.11.x

  • 0
    В качестве проверки realloc , realloc успеха хотя бы один раз, верно?
  • 0
    да. Он выдает следующие выходные данные за последние несколько циклов: ii = 0x7f3be8806010 jj = 0x7f3b68805010 ii = 0x7f3b68805010 jj = 0x7f3b68805010 * ii = 0 ii = 0x7f3b68805010 jj = (ноль) Ошибка сегментации (сбой ядра)
Показать ещё 6 комментариев
Теги:
realloc
segmentation-fault

3 ответа

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(). Как я только что видел, malloc() в 64-битных системах выделяет до 4GB памяти за один вызов, даже если вы можете выпустить несколько mallocs() и все равно добиться успеха на каждом из них.

Визуальная карта памяти пользовательского процесса, описанная в 32-битной системе Linux, описана в ответе, который я дал несколько дней назад: Является ли пространство ядра отображаемым в пространстве пользователя на 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

Очень близко к пределу 3 ГБ для 32-битных Linux-систем. В 64-битной системе Linux я достиг 30000 блоков по 4GB каждый и все еще рассчитываю. Я не знаю, сможет ли Linux выделить столько памяти или это моя ошибка. В соответствии с этим максимальное виртуальное адресное пространство составляет 128 ТБ (это будет 32768 блоков 4GB)


ОБНОВЛЕНИЕ: на самом деле, так оно и есть. Я оставил эту программу в 64-битном поле, а после 110074 успешно выделенных блоков, общее количество выделенной памяти было 131071.578884 GB. Каждый malloc() смог выделить до 4 GB на операцию, но когда он достиг 115256 GB, он начал выделять до 2 GB, а затем, когда он достиг 123164 GB, он выделил до 1GB на malloc(). Прогресс asyntotically имеет тенденцию к 131072 GB, но он фактически останавливается немного раньше, 131071.578884 GB потому что сам процесс, его данные и разделяемые библиотеки используют несколько КБ памяти.

Изображение 174551

  • 0
    Что ж, это отвечает на вопрос, почему происходит сбой, однако это оставляет меня перед новой проблемой: есть ли лучший способ просто извлечь точное количество нераспределенной памяти из системы?
  • 0
    Для этого последнего бита цитируем man-страницу для realloc: if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).
Показать ещё 2 комментария
1

Я думаю, что getrlimit() - ответ на ваш новый вопрос. Вот страница man http://linux.die.net/man/2/getrlimit

Вероятно, вас интересует RLIMIT_STACK. Проверьте страницу руководства.

0

первая часть заменяется следующим.

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню