Мне нужно выделить всю память, доступную для процесса, чтобы выполнить проверку системной службы. Тест (среди прочего) требует исчерпания всех доступных ресурсов, попыток вызова и проверки конкретного результата.
Чтобы сделать это, я написал цикл, который перераспределяет блок памяти до тех пор, пока 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
Вы не проверяете значение 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
потому что сам процесс, его данные и разделяемые библиотеки используют несколько КБ памяти.
if size is equal to zero, and ptr is not NULL, then the call is equivalent to free(ptr).
Я думаю, что getrlimit()
- ответ на ваш новый вопрос. Вот страница man 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);
realloc
,realloc
успеха хотя бы один раз, верно?