Мне необходимо выделить всю память, доступную для процесса, чтобы выполнить тест системного сервиса. Тест (среди прочего) требует исчерпания всех доступных ресурсов, попытки вызова и проверки конкретного результата.
Чтобы сделать это, я написал цикл, который перераспределяет блок памяти до тех пор, пока realloc не возвращает ноль, затем использует последнее правильное распределение, затем сокращает разницу между последним успешным количеством и последним неудачным количеством, пока неуспешное количество не станет 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);
}
Согласно man-странице realloc, realloc не уничтожает предыдущий адрес, если возвращает null. Тем не менее, этот код приводит к segfault, когда он пытается напечатать lastgood после того, как toalloc получает NULL от realloc. Почему это происходит, и есть ли лучший способ просто получить точное количество нераспределенной памяти?
Я запускаю его на glibc, на Ubuntu с ядром 3.11.x
Вы не проверяете значение 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()
, новое значение вершины снова равно 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()
, Прогрессия асинхронно имеет тенденцию к 131072 GB
, но на самом деле это останавливается немного раньше, 131071.578884 GB
потому что сам процесс, его данные и общие библиотеки, использует несколько КБ памяти.
Я думаю getrlimit()
это ответ на ваш новый вопрос. Вот справочная страница http://linux.die.net/man/2/getrlimit
Вы, вероятно, заинтересованы в RLIMIT_STACK
, Пожалуйста, проверьте страницу руководства.
Первая часть заменена на следующую.
char *p;
size_t size = SIZE_MAX;//SIZE_MAX : #include <stdint.h>
do{
p = malloc(size);
size >>= 1;
}while(p==NULL);