Использует ли Malloc только кучу, если требуемое пространство памяти велико? - программирование
Подтвердить что ты не робот

Использует ли Malloc только кучу, если требуемое пространство памяти велико?

Всякий раз, когда вы изучаете выделение памяти процессами, вы обычно видите это следующим образом:

enter image description here

Пока все хорошо.

Но тогда у вас есть системный вызов sbrk(), который позволяет программе изменять верхний предел раздела данных, и его также можно использовать, чтобы просто проверить, где это ограничение, с sbrk ( 0). Используя эту функцию, я нашел следующие шаблоны:

Шаблон 1 - Малый malloc

Я запускаю следующую программу на своей машине Linux:

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

int globalVar;

int main(){
        int localVar;
        int *ptr;

        printf("localVar address (i.e., stack) = %p\n",&localVar);
        printf("globalVar address (i.e., data section) = %p\n",&globalVar);
        printf("Limit of data section = %p\n",sbrk(0));

        ptr = malloc(sizeof(int)*1000);

        printf("ptr address (should be on stack)= %p\n",&ptr);
        printf("ptr points to: %p\n",ptr);
        printf("Limit of data section after malloc= %p\n",sbrk(0));

        return 0;
}

И вывод следующий:

localVar address (i.e., stack) = 0xbfe34058
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x91d9000
ptr address (should be on stack)= 0xbfe3405c
ptr points to: 0x91d9008
Limit of data section after malloc= 0x91fa000

Как вы видите, выделенная область памяти была выше предела старого раздела данных, а после того, как malloc этот предел был сдвинут вверх, поэтому выделенная область находится фактически внутри нового раздела данных.

Вопрос 1. Означает ли это, что маленькие маллоки будут выделять память в разделе данных и вообще не использовать кучу?

Шаблон 2 - Большой Malloc

Если размер запрашиваемой памяти увеличен в строке 15:

ptr = malloc(sizeof(int)*100000);

вы получите следующий результат:

localVar address (i.e., stack) = 0xbf93ba68
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x8b16000
ptr address (should be on stack)= 0xbf93ba6c
ptr points to: 0xb750b008
Limit of data section after malloc= 0x8b16000

Как вы можете видеть здесь, предел раздела данных не изменился, и вместо этого выделенная область памяти находится в середине раздела разрыва между секцией данных и стеком.

Вопрос 2: Это большой malloc, фактически использующий кучу?

Вопрос 3: Любое объяснение этого поведения? Я считаю это немного неуверенным, потому что на первом примере (small malloc) даже после того, как вы освободите выделенную память, вы все равно сможете использовать указатель и использовать эту память, не получая seg-ошибку, так как она будет внутри ваших данных раздел, и это может привести к затруднению обнаружения ошибок.

Обновление с помощью функций: Ubuntu 12.04, 32-бит, gcc версии 4.6.3, ядро ​​Linux 3.2.0-54-generic-pae.

Обновление 2: ответ Родриго ниже решил эту тайну. Эта ссылка в Википедии также помогла.

4b9b3361

Ответ 1

Прежде всего, единственный способ быть абсолютно уверенным в том, что происходит, - прочитать исходный код malloc. Или даже лучше, перейдите к нему с помощью отладчика.

Но во всяком случае, вот мое понимание этих вещей:

  • Системный вызов sbrk() используется для увеличения размера раздела данных, все в порядке. Обычно вы не будете называть его напрямую, но он будет вызван реализацией malloc(), чтобы увеличить память, доступную для кучи.
  • Функция malloc() не выделяет память из ОС. Он просто разбивает секцию данных на части и присваивает эти части тем, кто в них нуждается. Вы используете free() для обозначения одной части как неиспользуемой и доступной для переназначения.
  • Точка 2 является упрощением. По крайней мере, реализация GCC для больших блоков malloc() выделяет их с помощью mmap() с частными, не файловыми файлами. Таким образом, эти блоки находятся вне сегмента данных. Очевидно, вызов free() в таком блоке вызовет munmap().

Что такое большой блок, зависит от многих деталей. См. man mallopt для подробностей.

Из этого вы можете догадаться, что происходит, когда вы получаете доступ к свободной памяти:

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

Разъяснение

Раздел данных терминов используется с двумя разными значениями, в зависимости от контекста.

  • Раздел .data исполняемого файла (линкерная точка зрения). Он может также включать .bss или даже .rdata. Для ОС это ничего не значит, оно просто отображает фрагменты программы в память, не заботясь о том, что она содержит, кроме флагов (только для чтения, исполняемый файл...).
  • Куча, этот блок памяти, который есть у каждого процесса, который не читается из исполняемого файла, и который можно вырастить с помощью sbrk().

Вы можете видеть, что с помощью следующей команды, которая печатает макет памяти простой программы (cat):

$ cat /proc/self/maps
08048000-08053000 r-xp 00000000 00:0f 1821106    /usr/bin/cat
08053000-08054000 r--p 0000a000 00:0f 1821106    /usr/bin/cat
08054000-08055000 rw-p 0000b000 00:0f 1821106    /usr/bin/cat
09152000-09173000 rw-p 00000000 00:00 0          [heap]
b73df000-b75a5000 r--p 00000000 00:0f 2241249    /usr/lib/locale/locale-archive
b75a5000-b75a6000 rw-p 00000000 00:00 0 
b75a6000-b774f000 r-xp 00000000 00:0f 2240939    /usr/lib/libc-2.18.so
b774f000-b7750000 ---p 001a9000 00:0f 2240939    /usr/lib/libc-2.18.so
b7750000-b7752000 r--p 001a9000 00:0f 2240939    /usr/lib/libc-2.18.so
b7752000-b7753000 rw-p 001ab000 00:0f 2240939    /usr/lib/libc-2.18.so
b7753000-b7756000 rw-p 00000000 00:00 0 
b7781000-b7782000 rw-p 00000000 00:00 0 
b7782000-b7783000 r-xp 00000000 00:00 0          [vdso]
b7783000-b77a3000 r-xp 00000000 00:0f 2240927    /usr/lib/ld-2.18.so
b77a3000-b77a4000 r--p 0001f000 00:0f 2240927    /usr/lib/ld-2.18.so
b77a4000-b77a5000 rw-p 00020000 00:0f 2240927    /usr/lib/ld-2.18.so
bfba0000-bfbc1000 rw-p 00000000 00:00 0          [stack]

Первая строка - это исполняемый код (раздел .text).

Вторая строка - это данные только для чтения (.rdata) и некоторые другие разделы только для чтения.

Третья строка - это .data + .bss и некоторые другие записываемые разделы.

Четвертая строка - куча!

Следующие строки, те, у которых есть имя, являются файлами с отображением памяти или общими объектами. Те, у кого нет имени, вероятно, большие блоки памяти malloc'ed (или, возможно, частные анонимные mmap, их невозможно отличить).

Последняя строка - это стек!