LINUX.ORG.RU

[C] malloc

 


0

0

Вчера начитался ЛОРа и написал вот такую программу:

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

int main(int argc, char* argv[])
{
    unsigned int size;
    void* ptr;


    size = 1024 * 1024 * 1024;

    if ((ptr = (void*) malloc(size)) == NULL)
    {
        printf("Error due to initial memory allocation\n");
        exit(1);
    }

    memset(ptr, 0, size);
    printf("Allocated %d bytes\n", size);

    // Lets play a little game
    while (1)
    {
        printf("> ");
        scanf("%d", &size);
        if (size == 0)
        {
            break;
        }

        if ((ptr = realloc(ptr, size)) == NULL)
        {
            printf("Error due to memory allocation\n");
        }

        memset(ptr, 0, size);
        printf("Allocated %d bytes\n", size);
    }

    printf("Memory freed\n");
    free(ptr);
    exit(0);
}

Сутра показал другу, а он спросил меня как я дошел до такого :)

В процессе написания было замечено, что сразу после malloc память по показаниям не используется, а вот после заполнения ее уже заметно. Почему так происходит?

Что улучшить, на что бросить свои силы дальше?

Ответ на: комментарий от passshok

>Непонел. Поподробнее пожалуйста.

Если realloc вернёт NULL, то память, выделенная ранее, потеряется.

anonymous
()

В процессе написания было замечено, что сразу после malloc память по показаниям не используется


этим управляет ядро, оно начинает записывать данные в память только когда это начинает делать процесс.
Возьми выдели столько же, а в memset size/2 и посмотри

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от anonymous

> Если realloc вернёт NULL, то память, выделенная ранее, потеряется.

Понял, починю.

passshok
() автор топика
Ответ на: комментарий от Boy_from_Jungle

>этим управляет ядро, оно начинает записывать данные в память только когда это начинает делать процесс.

А ты что курил ?

bengan
()
Ответ на: комментарий от anonymous

Если realloc вернёт NULL, то память, выделенная ранее, потеряется.

Для realloc лучше использовать другой указатель, ИМХО.

Boy_from_Jungle ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

Можно поинтересоваться, как это происходит ? Первое, что приходит на ум - ядро выдаёт адреса левых страниц, при обращении к которым происходит прерывание, и дальше понеслось ?

* пошёл искать на полке Таненбаума

runtime ★★★★
()

Давай пойдем по порядку. Откуда система может взять память для твоей программы по твоему запросу?

1й путь это раздвинуть стек данных вызвав sbrk, это применяется на мелких переменных 2й путь это приммапить анонимный файл

Ты попытался выделить 1 гигабайт. Система просто примапила этот файл, анонимный, к твоему virtual memory space. Т.к. ты к этой памяти не обращася, то эти страницы не были еще подняты в память с анонимного файла. В момент обращения они будут подняты (фактически тут созданы). Как только у системы кончится память, она попытается опустить эти страницы, но они анонимны, и опускать их внекуда. Из-за этого придет злой OOM Killer и кого-то убъет.

Более очевидно, для тебя, решение это отключить overcommit в терминах памяти.

Вообще, можно, прочитать про постраничный доступ и модель виртуального адресного пространства.

catap ★★★★★
()
Ответ на: комментарий от passshok

> В случае нахождения прошу опубликовать результаты: :)

Ответа конкретно на этот вопрос там небыло, просто хочу освежить память о том (книгу то я нашёл, но читать наверное буду завтра с утра). Лично у меня сейчас в голове 2 варианта реализации:

ядро получает запрос на расширение BSS (или как там его) от malloc; страницы при этом не выделяются, ядро просто в одной из структур запоминает новый размер. при последующем обращении к невыделенной странице происходит прерывание. ядро смотрит в чём дело - если обращение было к странице, которая «как-бы» была выделена, то происходит выделение памяти и BSS уже расширяется «физически». В описании немного (ок, ок, очень сильно) неточно использована терминология, но, думаю, идея ясна.

Пока писал, понял, что второй подход о котором я думал не катит.

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

runtime ★★★★
()

Задам связанный вопрос. Часто при сдаче олимпиадных задач со СТАТИЧЕСКИМ выделением памяти (то есть просто создается, например, int a[50000][50000], (размеры от балды, в реальных задачах другие) первые тесты проходятся и memory limit exceeded вылазит где-нибудь ближе к концу проверки. Почему?

unikoid ★★★
()
Ответ на: комментарий от catap

При том, что только ядро может выделять программе память на ходу, по мере обращения к ней.

PS: мы сейчас вообще о чём говорим ? :)

runtime ★★★★
()
Ответ на: комментарий от runtime

> int array[1000000000]; array[0] = 10;
> main.cpp:7: error: size of array ‘array’ is too large
> int array[10000000]; array[0] = 10;
> Segmentation fault

прекратите стек мучать :) «static int array[1000000000];» — полёт нормальный ;)

arsi ★★★★★
()
Ответ на: комментарий от arsi

[code=cpp]int array[5000][5000];

int main(int argc, char * argv[]) {    array[0][0] = 10;    //array[49999][4999] = 7;    cout<<array[0][0];    //cout<<array[49999][49999]; [/code]

Если раскоментировать - segfault.

2 unikoid: Не иначе как оптимизация

runtime ★★★★
()
Ответ на: комментарий от catap

Ладно, ок, если я несу бред - не трать на него время, завтра я перечитаю Таненбаума и разберусь

runtime ★★★★
()
Ответ на: комментарий от runtime

> Если раскоментировать - segfault.

марш в магазин за планкой памяти на 8 гиг ;) можно, правда, своп увеличить, но это не так интересно :)

arsi ★★★★★
()
Ответ на: комментарий от arsi

> марш в магазин за планкой памяти на 8 гиг ;)

Не, я гордый владелец целых 512mb, мне больше не надо :)

runtime ★★★★
()
Ответ на: комментарий от runtime

Пардон, тут у меня опечатка. Ну да ладно, переделывать лень.

runtime ★★★★
()
Ответ на: комментарий от runtime

Ну дык конечно, ибо унутре main. Stack overflow получается. А вот если глобально, то прокатывает.
//Ну во всяком случае тестирующая система не фиксирует MLE до какого-то момента.

unikoid ★★★
()
Ответ на: комментарий от arsi

>марш в магазин за планкой памяти на 8 гиг ;) можно, правда, своп увеличить, но это не так интересно :)
Так вроде ж еще есть ограничение на количество памяти для процесса, не?
//Если говорю глупости, не бейте по почкам, первый курс же.

unikoid ★★★
()
Ответ на: комментарий от unikoid

> Так вроде ж еще есть ограничение на количество памяти для процесса, не?

конечно же есть! и по дефолту оно установлено в unlimited! :)

зы: только что успешно выделил 10 гиг (через статический массив).

arsi ★★★★★
()
Ответ на: комментарий от catap

>>А причем тут ядро, а?

Ядро тут при том, что только оно способно прицепить твоему процессу нужное количество страниц памяти, и если посчитает нужным, то может и послать. Так-то.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от MuZHiK-2

и если посчитает нужным,

оно проверяет и пытается, и может даже тебе обнулить её если захош.
И вообще ядро мне нравиться, оно пытается удовлетворить тебя любыми способами :)

Boy_from_Jungle ★★★★
()

кстати, прошу у всех прощения; мой первый пост был ответом на:

сразу после malloc память по показаниям не используется, а вот после заполнения ее уже заметно. Почему так происходит?

limux
()
Ответ на: комментарий от catap

> 1й путь это раздвинуть стек данных вызвав sbrk, это применяется на мелких переменных 2й путь это приммапить анонимный файл

Спасибо, про мапинг анонимных файлов (и то что malloc может его использовать) я не знал, теперь разобрался.

runtime ★★★★
()
Ответ на: комментарий от Boy_from_Jungle

>>И вообще ядро мне нравиться, оно пытается удовлетворить тебя любыми способами :)

Именно от этого я тоже тащусь :) Кстати, про маппинг вопрос спорный, не все реализации malloc и не всегда используют его.

MuZHiK-2 ★★★★
()
Ответ на: комментарий от limux

Это же, но по-русски:

Уступающее выделение памяти

В Linux применяется стратегия уступающего выделения памяти (opportunistic allocation). Когда процесс запрашивает у ядра дополнительную память, например, увеличивая свой сегмент данных или создавая новое отображение в памяти, ядро фиксирует обязательство(commit) выделить память, не предоставляя фактически никакое хранилище. Только когда процесс начинает записывать, ядро удовлетворяет (satisfy) запрос, преобразуя обязательство выделить память в физическое выделение памяти. Ядро делает это постранично, при необходимости выполняя отложенную подкачку страниц и копирование при записи.

У такого поведения есть несколько преимуществ. Во-первых, ленивое выделение памяти позволяет ядру откладывать большую часть работы до последнего момента — если вообще когда-нибудь действительно понадобится удовлетворять запросы на выделение памяти. Во-вторых, так как запросы удовлетворяются постранично и по требованию, только действительно потребляемая физическая память занимает физическое хранилище. Наконец, объем зафиксированной памяти может значительно превосходить объем физической памяти и даже доступного пространства подкачки. Эта последняя особенность называется принятием чрезмерных обязательств (overcommitment).

edigaryev ★★★★★
()
Ответ на: комментарий от catap

> Как только у системы кончится память, она попытается опустить эти страницы, но они анонимны, и опускать их внекуда.

Если своп отключен, о чем речи не было.

tailgunner ★★★★★
()
Ответ на: комментарий от MuZHiK-2

Послать ядро, конечно может, но это надо совсем рожей не выйти. Т.е. ты можешь сказать malloc(100500*100500) и оно скажет, заебися! Вот правда при попытке пробежаться по этой памяти, да, прибежит OOM Killer...

catap ★★★★★
()
Ответ на: комментарий от tailgunner

Ну swap-то не бесконечен, правда? И рано или поздно опускать можно будет только внекуда.

Опять же, а кто использует, в жизни, swap? Оно же тормозить будет! Лучше oom killer и потом мысли про жизнь, чем не прогнозируемые тормоза у тазика

catap ★★★★★
()
Ответ на: комментарий от runtime

Для закрепления знаний, предлагаю, написать свой malloc/free :)

Я, кстати, серьезно. Помогает разобраться.

catap ★★★★★
()
Ответ на: комментарий от catap

> Что не нравится в фразе?

А что может нравиться в словосочетании «анонимные файлы»? Мало того, что они анонимные, так их еще и много (как их различают, интересно).

Не существует никаких «анонимных файлов», ни единого. Маппинг либо файловый (нормального именованного файла), либо анонимный. Блин, ну вы бы хоть man mmap прочитали:

MAP_ANONYMOUS The mapping is not backed by any file; [...] The fd and offset arguments are ignored;

tailgunner ★★★★★
()
Ответ на: комментарий от catap

> Для закрепления знаний, предлагаю, написать свой malloc/free :)

Предлагаешь только через mapping, или вместе с brk ? Вообще, может и попробую.

runtime ★★★★
()
Ответ на: комментарий от balodja

Ах, да, страница 277 в бумажной нумерации(или 296 в pdf'ной). Заголовок «Opportunistic allocation».

balodja ★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.