У меня есть эта простая программа C:Различного поведения перераспределить в Linux и OSX
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main (int argc, char **argv) {
int i = 0;
int j = 0;
size_t size = 4194304; /* 4 MiB */
char *buffer = malloc(size);
char *buffers[10] = {NULL};
void *tmp_pointer = NULL;
fprintf(stderr, "initial size == %zu\n", size);
fprintf(stderr, "initial buffer == %p\n\n", buffer);
srand(time(NULL));
/* let's see when it fails ... */
for (; i < 10; ++i) {
/* some random writes */
for (j = 0; j < 1000; ++j) {
buffer[rand() % size] = (char)(rand());
}
/* some interleaving memory allocations */
buffers[i] = malloc(1048576); /* 1 MiB */
size *= 2;
fprintf(stderr, "new size == %zu\n", size);
tmp_pointer = realloc(buffer, size);
if ((tmp_pointer == NULL) || (errno != 0)) {
fprintf(stderr, "tmp_pointer == %p\n", tmp_pointer);
fprintf(stderr, "errno == %d\n", errno);
perror("realloc");
return (1);
} else {
buffer = tmp_pointer;
}
fprintf(stderr, "new buffer == %p\n\n", buffer);
}
fprintf(stderr, "Trying to free the buffers.\n");
free(buffer);
if (errno != 0) {
fprintf(stderr, "errno == %d\n", errno);
perror("free(buffer)");
return (2);
}
for (i = 0; i < 10; ++i) {
free(buffers[i]);
if (errno != 0) {
fprintf(stderr, "i == %d\n", i);
fprintf(stderr, "errno == %d\n", errno);
perror("free(buffers)");
return (3);
}
}
fprintf(stderr, "Successfully freed.\n");
return (0);
}
Он просто выделяет 4 МиБа памяти и в 10 раз пытаются удвоить его размер перераспределения. Простые вызовы realloc чередуются с другими выделениями из 1 блоков MiB и некоторыми случайными записями, чтобы минимизировать «трюки» распределителя кучи. На Linux машины Ubuntu с 16 GiB RAM, у меня есть следующий вывод:
./realloc_test
initial size == 4194304
initial buffer == 0x7f3604c81010
new size == 8388608
new buffer == 0x7f3604480010
new size == 16777216
new buffer == 0x7f360347f010
new size == 33554432
new buffer == 0x7f360147e010
new size == 67108864
new buffer == 0x7f35fd47d010
new size == 134217728
new buffer == 0x7f35f547c010
new size == 268435456
new buffer == 0x7f35e547b010
new size == 536870912
new buffer == 0x7f35c547a010
new size == 1073741824
new buffer == 0x7f3585479010
new size == 2147483648
new buffer == 0x7f3505478010
new size == 4294967296
new buffer == 0x7f3405477010
Trying to free the buffers.
Successfully freed.
Таким образом, все перераспределения до 4 ГиБ, казалось бы, успех. Однако, когда я установил ядро виртуального режима учета памяти 2 (всегда проверяйте, никогда не overcommit) с помощью этой команды:
echo 2 > /proc/sys/vm/overcommit_memory
тогда выходные изменения:
./realloc_test
initial size == 4194304
initial buffer == 0x7fade1fa7010
new size == 8388608
new buffer == 0x7fade17a6010
new size == 16777216
new buffer == 0x7fade07a5010
new size == 33554432
new buffer == 0x7fadde7a4010
new size == 67108864
new buffer == 0x7fadda7a3010
new size == 134217728
new buffer == 0x7fadd27a2010
new size == 268435456
new buffer == 0x7fadc27a1010
new size == 536870912
new buffer == 0x7fada27a0010
new size == 1073741824
new buffer == 0x7fad6279f010
new size == 2147483648
tmp_pointer == (nil)
errno == 12
realloc: Cannot allocate memory
rellocation терпит неудачу на 2 Гб , Свободная память компьютера в то время, о котором сообщает top
, составляет около 5 ГБ, поэтому разумно, потому что realloc всегда должен выделять непрерывный блок памяти. Теперь давайте посмотрим, что происходит при выполнении той же программы на Mac OS X Lion внутри VirtualBox на той же машине, но только с 8 Гб на виртуальной памяти:
./realloc_test
initial size == 4194304
initial buffer == 0x101c00000
new size == 8388608
tmp_pointer == 0x102100000
errno == 22
realloc: Invalid argument
Здесь, эта программа имеет проблемы с очень первый realloc до 8 MiB. Это, на мой взгляд, очень странно, потому что свободная память виртуального компьютера в то время, как сообщается top
, составляет около 7 GiB.
Правда, однако, что realloc фактически преуспел, поскольку его возвращаемое значение не равно NULL (обратите внимание на значение tmp_pointer
непосредственно перед завершением программы). Но тот же самый успешный вызов realloc также установил errno в ненулевое значение! Теперь, каков правильный способ справиться с этой ситуацией?
Должен ли я просто игнорировать errno и проверять только возвращаемое значение из realloc? Но тогда как насчет некоторых следующих обработчиков ошибок, основанных на ошибках? Это, вероятно, не очень хорошая идея.
Должен ли я устанавливать errno в ноль, когда realloc возвращает указатель не-NULL? Кажется, это решение. Но ... Я посмотрел здесь: http://austingroupbugs.net/view.php?id=374. Я не знаю, насколько авторитетным является этот ресурс, но в отношении realloc это очень ясно:
«... стандарт также явственен, что errno не может быть проверен при успешном завершении, если не зарегистрировано, ...»
Если я правильно понимаю, в нем говорится: Да, когда realloc возвращает NULL, вы можете посмотреть на errno, но не иначе! Это сказало, я могу сбросить errno к нулю? Не смотря на это? Мне очень непонятно понимать и решать, что плохо, и что хорошо делать.
Я все еще не могу понять, почему realloc устанавливает это errno в первую очередь. И что означает его значение «Недопустимый аргумент»? Он не указан в man-страницах, они упоминают только errno ENOMEM (обычно число 12). Может что-то пойдет не так? Что-то в этой простой программе вызывает это поведение в Mac OS X? Вероятно, да, ... так что два основных вопроса:
- Что не так? и
- Как исправить?Точнее: как улучшить эту простую программу, чтобы realloc в Mac OS X оставил errno при нулевом значении?
Хорошо, я понимаю, что 'errno' определяется только в случае сбоя. Это имеет смысл. –
Также имеет смысл сначала проверить возвращаемое значение realloc и только проверять 'errno' в случае сбоя realloc (возвращаемое значение NULL). –
Но я действительно должен не соглашаться с тем, что вы сказали об успешном вызове realloc и errno, который был нетронутым (не обязательно нулевым, но неизменным). Как я уже сказал в своем вопросе, в Mac OS X Lion это не всегда верно. Вопрос в том, что вызывает это странное поведение и как его избежать. –