Почему malloc и sbrk возвращают адрес из отдельных сегментов?

Я пытаюсь понять, как происходит динамическое выделение памяти. Поэтому я подумал о реализации собственного malloc, используя sbrk() системный вызов. Мой вопрос здесь в том, что когда я пытаюсь выделить динамическую память, sbrk () и malloc () не непрерывно возвращают разные адреса.

Вот мой код

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

int main(int argc, char** argv)
{
    printf("\nsbrk(0) %llu ",(unsigned long long)sbrk(0));
    printf("\nmalloc(8) %llu ",(unsigned long long)malloc(8));
    printf("\nmalloc(8) %llu ",(unsigned long long)malloc(8));
    printf("\nsbrk(8) %llu ",(unsigned long long)sbrk(8));
    printf("\nmalloc(8) %llu ",(unsigned long long)malloc(8));
    printf("\nmalloc(8) %llu ",(unsigned long long)malloc(8));  
    printf("\n");
    return 0;
}

Вывод приведенного выше кода

sbrk(0) 30306304 
malloc(8) 30306320 
malloc(8) 30306352 
sbrk(8) 30441472 
malloc(8) 30306384 
malloc(8) 30306416 

Может ли кто-нибудь объяснить, почему sbrk(8) не является непрерывным местоположением.


person Sadaananth Anbucheliyan    schedule 22.05.2019    source источник
comment
Современные распределители POSIX обычно используют для своего распределения mmap, а не sbrk.   -  person Some programmer dude    schedule 22.05.2019
comment
Кстати, правильный printf формат для печати указателя (void *) - "%p".   -  person Some programmer dude    schedule 22.05.2019


Ответы (2)


Стандарт не дает никаких гарантий непрерывности хранилища даже для памяти, выделенной последовательными вызовами malloc. Таким образом, различные вызовы malloc в вашем коде не обязательно должны приводить к смежным местоположениям.

Стандарт C11 гласит:

7.22.3 Функции управления памятью

1. Порядок и непрерывность памяти, выделенной последовательным вызовы функций aligned_alloc, calloc, malloc и realloc не определены.

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

person P.W    schedule 22.05.2019

Предполагая, что вы работаете в Linux, причина относительно большой разницы в расположении памяти из malloc() и sbrk() в том, что реализация glibc malloc() использует sbrk() внутри для получения памяти, которая функции, такие как malloc(), возвращаются вызывающей стороне. Например, предположим, что первоначальная внутренняя реализация glibc получает 32 МБ кучи через sbrk(), а память, возвращенная из malloc(), будет находиться в этом фрагменте размером 32 МБ. Если вы затем используете sbrk() для получения памяти, она будет из памяти, вновь выделенной в конце исходного блока размером 32 МБ, поэтому адреса из malloc() и sbrk() будут отличаться.

Обратите внимание, что вы не можете безопасно смешивать использование malloc()calloc(), realloc() и т. Д.) И sbrk(), поскольку внутренняя реализация malloc() использует sbrk() для получения памяти, возвращаемой через malloc(). Согласно malloc() странице руководства Linux:

Обычно malloc() выделяет память из кучи и при необходимости регулирует размер кучи, используя sbrk(2). При выделении блоков памяти размером более MMAP_THRESHOLD байта реализация glibc malloc() выделяет память как частное анонимное отображение, используя mmap(2). MMAP_THRESHOLD по умолчанию составляет 128 КБ, но его можно настроить с помощью mallopt(3). До Linux 4.7 на распределение ресурсов, выполняемое с использованием mmap(2), не влияло RLIMIT_DATA ограничение ресурсов; начиная с Linux 4.7 этот предел также применяется для выделения памяти, выполняемого с использованием mmap(2).

Когда вы смешиваете malloc() и sbrk() в Linux для получения памяти, вы, скорее всего, повредите кучу процесса.

person Andrew Henle    schedule 22.05.2019