Использует ли malloc() brk() или mmap()?

c code:

// program break mechanism
// TLPI exercise 7-1

#include <stdio.h>
#include <stdlib.h>

void program_break_test() {
    printf("%10p\n", sbrk(0));

    char *bl = malloc(1024 * 1024);
    printf("%x\n", sbrk(0));

    free(bl);
    printf("%x\n", sbrk(0));

}

int main(int argc, char **argv) {
    program_break_test();
    return 0;
}

При компиляции следующего кода:

 printf("%10p\n", sbrk(0));

Я получаю подсказку:

format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’

Вопрос 1: Почему это?


И после я malloc(1024 * 1024) кажется, что разрыв программы не изменился.

Вот результат:

9b12000
9b12000
9b12000

Вопрос 2: Распределяет ли память память в куче при запуске для будущего использования? Или компилятор изменит временной интервал для выделения? В противном случае, почему?


[обновление] Резюме: brk() или mmap()

После просмотра TLPI и проверки man-страницы (с помощью автора TLPI), теперь я понимаю, как malloc() решить использовать brk() или mmap(), как показано ниже:

mallopt() может устанавливать параметры для управления поведением malloc(), и есть параметр с именем M_MMAP_THRESHOLD, в общем случае:

  • Если запрошенная память меньше, чем brk(),
  • Если запрошенная память больше или равна ей, будет использоваться mmap();

Значение по умолчанию параметра 128kb (в моей системе), но в моей тестовой программе я использовал 1Mb, поэтому был выбран mmap(), когда я изменил запрашиваемую память на 32kb, я увидел, что brk() будет б.

В книге упоминалось, что в TLPI стр. 147 и 1035, но я не читал внимательно эту часть.

Подробную информацию о параметре можно найти на странице руководства для mallopt().

Ответ 1

Если мы изменим программу, чтобы увидеть, где находится malloc 'd memory:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void program_break_test() {
  printf("%10p\n", sbrk(0));

  char *bl = malloc(1024 * 1024);
  printf("%10p\n", sbrk(0));
  printf("malloc'd at: %10p\n", bl);

  free(bl);
  printf("%10p\n", sbrk(0));

}

int main(int argc, char **argv) {
  program_break_test();
  return 0;
}

Возможно, немного яснее, что sbrk не изменится. Память, предоставленная нам malloc, отображается в совершенно другое место.

Вы также можете использовать strace в Linux, чтобы узнать, какие системные вызовы сделаны, и узнайте, что malloc использует mmap для выполнения выделения.

Ответ 2

malloc не ограничивается использованием sbrk для выделения памяти. Например, можно использовать mmap для отображения большого блока памяти MAP_ANONYMOUS; обычно mmap назначит виртуальный адрес вдали от сегмента данных.

Есть и другие возможности. В частности, mmap, являясь основной частью стандартной библиотеки, сам по себе не ограничивается стандартными библиотечными функциями; он может использовать интерфейсы, зависящие от операционной системы.

Ответ 3

format '% p ожидает аргумент типа' void *, но аргумент 2 имеет тип 'int

Ответ на вопрос 1. Компилятор сообщает вам, что аргумент должен быть void *, но вместо этого вы предоставляете int. Это должно быть очевидно, если вы потратите пять секунд на чтение и понимание ошибки. Есть ли какая-то часть того, что вы не понимаете? Если да, задайте более подробный вопрос о том, что вас смущает, а не "Почему это?"...

Аналогичное предупреждение должно появиться для printf("%x\n", sbrk(0));, так как согласно руководство, %x, как ожидается, будет соответствовать unsigned параметр. Кроме того, согласно руководству:

Если какой-либо аргумент не является правильным типом для соответствующей спецификации преобразования, поведение undefined.

Обычно мы должны стремиться писать программы, которые работают одинаково в любой системе. Для этого нам необходимо создать набор правил. Следовательно, выдается предупреждение о том, что вы нарушаете правила и вызываете поведение undefined. Несмотря на поведение undefined, ваш код может работать так, как вы ожидаете, в вашей системе на данный момент времени... Однако на это нельзя полагаться, потому что в какой-то момент в будущем ваш компьютер может извлечь обновление, которое заставляет ваш код разбиваться на тонкие, но разрушительные способы, или может не работать на других компьютерах... или может просто выбрать время в этом месяце.


Ответы на вопросы 2, 3 и 4:

Распределяет ли память память в куче при запуске для будущего использования?

Нет требования, чтобы "куча" существовала в рамках правил стандарта C, поэтому "нет"... По крайней мере, пока вы не сообщите нам, какую компилятор/стандартную библиотеку вы используете.

Или компилятор изменит временной интервал для выделения?

Возможно. Составителям разрешено выполнять оптимизацию, которая может даже устранить ваше распределение, при условии, что они могут сделать вывод, что это безопасно (например, наблюдаемое поведение не изменяется).

В противном случае, почему?

Хороший вопрос.

Почему у нас есть правила, которые могут свободно рассматривать поведение undefined и позволяют ему запускаться, даже на пороге программиста? Оптимизация.

Почему имеет значение, идет ли память, которую вы выделяете, в кучу или иначе? Зачем вам это нужно? До тех пор, пока выделена ваша память, не так ли? Пока вы можете использовать его, и это достаточно быстро. Оптимизация.

Почему компилятор выполняет оптимизацию? Я оставлю это для вас, чтобы ответить;)

Ответ 4

Если вы используете malloc в своем коде, он вначале вызовет brk(), выделив 0x21000 байт из кучи, чтобы адрес, который вы напечатали, поэтому вопрос 1: следующие требования malloc могут быть выполнены из предварительно выделенного пространства, так что эти malloc фактически не вызывали brk, это оптимизация в malloc. Если в следующий раз вы захотите увеличить размер malloc за эту границу, будет вызван новый brk (если он не больше порога mmap).