LINUX.ORG.RU

Как выделяется память в линуксе?


2

3

Помогите, пожалуйста, разобраться. Есть 2 программки, одна просто выделяет себе всю доступную память, другая пытается при этом сразу эту память использовать. malloctest.c

#define SIZE 4096
int main(){
    int i, oldi;
    void* ma[SIZE];
    for(i=0; i<SIZE; i++){
        ma[i]=malloc(1024*1024);
        if(ma[i]==NULL)
            break;
    }
    oldi=i;
    printf("allocated %i times 1M = %i\n", i, i*1024*1024);
    for(; i<SIZE; i++){
        ma[i]=malloc(1024);
        if(ma[i]==NULL)
            break;
    }
    printf("allocated %i times 1k = %i\n", i-oldi, (i-oldi)*1024);
    printf("total allocated %i\n", (oldi*1024*1024) + (i-oldi)*1024);
    for(; i!=0; i--)
        free(ma[i]);
}
и malloctest_w_use.c
#define SIZE 4096
int main(){
    int i, oldi;
    void* ma[SIZE];
    for(i=0; i<SIZE; i++){
        ma[i]=malloc(1024*1024);
        memset(ma[i], 0, 1024*1024);
        if(ma[i]==NULL)
            break;
        printf("%i ok\n ", i);
    }
    oldi=i;
    printf("allocated %i times 1M = %i\n", i, i*1024*1024);
    for(; i<SIZE; i++){
        ma[i]=malloc(1024);
        memset(ma[i], 0, 1024);
        if(ma[i]==NULL)
            break;
    }
    printf("allocated %i times 1k = %i\n", i-oldi, (i-oldi)*1024);
    printf("allocated totally %i\n", (oldi*1024*1024) + (i-oldi)*1024);
    for(; i!=0; i--)
        free(ma[i]);
}
имеем
$ cat /proc/sys/vm/overcommit_memory 
2
$ cat /proc/sys/vm/overcommit_ratio 
85
$ free
             total       used       free     shared    buffers     cached
Mem:       2072164    1085868     986296          0      32640     222244
-/+ buffers/cache:     830984    1241180
Swap:            0          0          0
$ grep Commit /proc/meminfo
CommitLimit:     1761336 kB
Committed_AS:     911168 kB
Т.е. overcommit отключен. полностью свободной памяти > 960Mb, но можно ещё освободить.
$ ./malloctest
allocated 744 times 1M = 780140544
allocated 506 times 1k = 518144
total allocated 780658688
$ ./malloctest_w_use
0 ok
...
652 ok
653 ok
Segmentation fault

Во-первых, почему не выделяется вся доступная память? Её остаётся ~200 Мб свободной. Во-вторых, (хотя причина наверняка та же) почему программка сегфолтится?

И общие наблюдения следущие: когда загрузка памяти подходит к ~1Гб без буферов и кешей, программы начинают ругаться «failed to allocate...» или падать в сегфолт. Почему такое может быть? Ядро самосборное, включена поддержка памяти до 4Гб.

Ткните меня в мануал, где бы об этом писалось. Статьи Дреппера до конца не дочитал, но быстро просмотрев, не нашёл там возможных причин.

Во-первых, почему не выделяется вся доступная память? Её остаётся ~200 Мб свободной.

Слышал, что выделяются только непрерывные [в физической памяти] куски.

pacify ★★★★★
()

остальная резервируется для рута

anonymous
()

почему программка сегфолтится?

Мужык, ты сначала делаешь memset, а потом проверяешь указатель на NULL.

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

Мужык, ты сначала делаешь memset, а потом проверяешь указатель на NULL.

Ух ты ж! Спасибо, исправил. Теперь работают одинаково.

Однако:
$ cat /proc/sys/vm/overcommit_ratio
95
$ free
total used free shared buffers cached
Mem: 2072164 937752 1134412 0 33396 234712
-/+ buffers/cache: 669644 1402520
Swap: 0 0 0
$ ./malloctest
...
total allocated 982773760
$ sudo free
total used free shared buffers cached
Mem: 2072164 945072 1127092 0 33472 234712
-/+ buffers/cache: 676888 1395276
Swap: 0 0 0
$ sudo ./malloctest
...
total allocated 1051321344

Т.е. опять не выделяет всю память.

И что может быть с дурацким лимитом в 1гб?

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

Слышал, что выделяются только непрерывные [в физической памяти] куски.

я там пробую 1кб куски получать, а pagesize - целых 4кб.

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

total allocated 982773760

Ну всё правильно. Раньше было:

> total allocated 780658688

И что может быть с дурацким лимитом в 1гб?

А что с ним не так?

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

Ну всё правильно.

Так и памяти свободной стало больше: 1134412 - 982773760

А что с ним не так?

вот это вот:

когда загрузка памяти подходит к ~1Гб без буферов и кешей, программы начинают ругаться «failed to allocate...» или падать в сегфолт.

Т.е. судя по free, свободная память ещё имеется, но приложения падают из-за нехватки оной. По мне, так это не нормально. Я именно из-за этого и стал разбираться с работой памяти.

Ещё вот что нагуглил: http://stackoverflow.com/questions/6806391/oom-killer-strikes-although-overco... . Что это за IO waiting buffers? С ходу ничего не нагуглилось.

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

судя по free, свободная память ещё имеется, но приложения падают из-за нехватки оной. По мне, так это не нормально.

Зависит. Если с отключенным overcommit приложение пытается зохавать 1Г памяти, ему отказывают, оно падает. Смотри strace, какой запрос приложение выдает перед падением.

Что это за IO waiting buffers? С ходу ничего не нагуглилось.

Вот это ХЗ - возможно, процент памяти, выделенной под FS cache; может, он говорил о swappiness.

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

Странно, что-то оно внезапно заработало с памятью свыше 1Гб. Я ещё поиграюсь.

А всего изменений - только overcommit_ratio с 85 до 95. Но он ведь считается от всей памяти, а не только свободной?!
$ grep Commit /proc/meminfo
CommitLimit: 1968552 kB
Committed_AS: 1641480 kB
а было
CommitLimit: 1761336 kB
Committed_AS: 911168 kB

Кстати, почему Commited_AS больше чем used с буферами во free, 1641480 kB против 1190056?

byteworm
() автор топика

Ну так маны-то читать надо. Если, как ты говоришь, overcommit отключен (я понимаю, это значит vm.overcommit = 2), то максимальный объем выделяемой памяти по-умолчанию = объем свопа + 1/2 объема оперативы. Своп ты по чьему-то идиотскому совету отключил, теперь подумай сам.

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

Но он ведь считается от всей памяти, а не только свободной?!

Да, overcommit_ratio ты поправил...

Если я правильно понимаю, эта переменная задает максимальный размер коммита, если достаточно свободной памяти. У тебя, очевидно, нет 1.8 Гб свободной.

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

почему Commited_AS больше чем used с буферами во free, 1641480 kB против 1190056?

Ты даешь обрывки информации, из которой невозможно сделать вывод.

tailgunner ★★★★★
()

Потому что с этими настройками оно работает так. Суммируется виртуальная (!!!!) память всех процессов и если она будет превышать (память+своп)*0.85, то новая аллокация будет фейлиться.

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

In mode 2 (available since Linux 2.6), the total virtual address space on the system is limited to (SS + RAM*(r/100)), where SS is the size of the swap space, and RAM is the size of the physical memory, and r is the contents of the file /proc/sys/vm/overcommit_ratio

Я понимаю это так, что каждый процесс может получить 2Гб*0.85 = 1740Мб вне зависимости от размера свободной памяти.

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

Суммируется виртуальная (!!!!) память всех процессов

Но ведь виртуальная память, она per process basis. Это точно так, как ты описал?

byteworm
() автор топика
Ответ на: комментарий от tailgunner
$ free
             total       used       free     shared    buffers     cached
Mem:       2072164    1286856     785308          0      41144     246088
-/+ buffers/cache:     999624    1072540
Swap:            0          0          0
$ cat /proc/meminfo 
MemTotal:        2072164 kB
MemFree:          785184 kB
Buffers:           41152 kB
Cached:           246084 kB
SwapCached:            0 kB
Active:          1123732 kB
Inactive:         109124 kB
Active(anon):     945632 kB
Inactive(anon):      848 kB
Active(file):     178100 kB
Inactive(file):   108276 kB
Unevictable:           0 kB
Mlocked:               0 kB
HighTotal:       1186632 kB
HighFree:         344556 kB
LowTotal:         885532 kB
LowFree:          440628 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:               292 kB
Writeback:             0 kB
AnonPages:        945644 kB
Mapped:            53352 kB
Shmem:               856 kB
Slab:              23788 kB
SReclaimable:      14852 kB
SUnreclaim:         8936 kB
KernelStack:         968 kB
PageTables:         2364 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:     1968552 kB
Committed_AS:    1319600 kB
VmallocTotal:     122880 kB
VmallocUsed:       14756 kB
VmallocChunk:      95228 kB
AnonHugePages:    335872 kB
DirectMap4k:      589816 kB
DirectMap4M:      319488 kB

used - 1286856; Committed_AS - 1319600

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

да, так

Вот тестовый пример. Физической памяти он не ест, а виртуальную хавает. Вторую копию запустить уже не получится.

$ cat malloc.c
#include <stdio.h>
#include <stdlib.h>

int main()
{
        int r;
        int i;
        char * mem = malloc(1024L*1024L*1024L*3);
        int j = 0;
        for (i = 0; i < 1024*1024; i++) {
                *mem = 10;
        }
        for (i = 0; i < 1024*1024; i++) {
                j += *mem;
        }
        scanf("%d", &r);

        fprintf(stderr, "%p, %d", mem, r);
}
Reset ★★★★★
()
Ответ на: комментарий от byteworm

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

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

Поведение как раз будет как у byteworm. free будет показывать, что почти вся оператива свободна, а malloc уже не работает. Да, размер выделяемой памяти в примере надо подгонять под размер своей оперативы.

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

Поведение как раз будет как у byteworm

На системе с отключенным overcommit сам акт выделения памяти выделяет физическую память (если я правильно понимаю то, что ты хотел сделать).

На системе с включенным overcommit я запустил 3 экземпляра твоей программы (но пришлось поправить 3Г на 2Г - у меня 32 бита и поддержка 4Г не включена).

Да, размер выделяемой памяти в примере надо подгонять под размер своей оперативы.

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

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

На системе с отключенным overcommit сам акт выделения памяти выделяет физическую память (если я правильно понимаю то, что ты хотел сделать).

Это еще почему ?

На системе с включенным overcommit я запустил 3 экземпляра твоей программы (но пришлось поправить 3Г на 2Г - у меня 32 бита и поддержка 4Г не включена).

да, и что в этом удивительного?

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

Нет, тогда размер виртуальной памяти процесса почти сравняется с размером физической памяти.

cast catap

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

На системе с отключенным overcommit сам акт выделения памяти выделяет физическую память (если я правильно понимаю то, что ты хотел сделать).

Что за новости. Физическая память выделяется только по необходимости. И по необходимости же отнимается. Зпасти, например, tail /dev/zero, и смотри, как у всех процессов будет сокращаться RES.

Оверкоммит влияет только на порог выделения суммарной вирт.памяти (выключает или включает оный).

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

Оверкоммит влияет только на порог выделения суммарной вирт.памяти (выключает или включает оный).

Ты не понял, а объяснять долго.

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

На системе с отключенным overcommit сам акт выделения памяти выделяет физическую память

Это еще почему ?

Потому что он для этого сделан.

На системе с включенным overcommit я запустил 3 экземпляра твоей программы (но пришлось поправить 3Г на 2Г - у меня 32 бита и поддержка 4Г не включена).

да, и что в этом удивительного?

Ты сказал:

Reset> Вторую копию запустить уже не получится.

про overcommit ты не говорил ничего. Твоей программе нужны какие-то особые условия, чтобы ее вторая копия не запустилась?

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

Потому что он для этого сделан.

нет

про overcommit ты не говорил ничего. Твоей программе нужны какие-то особые условия, чтобы ее вторая копия не запустилась?

Говорил. Цитирую «Потому что с _этими_ настройками оно работает так...»

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

Позволь только спросить - у тебя на скольких машинах отключен overcommit?

Гениальный вопрос. На двух. Тебе стало легче? Или ты тренируешься в телепатии?

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

Потому что он для этого сделан.

нет

Хы. Ну, как скажешь %)

«Потому что с _этими_ настройками оно работает так...»

Ты не мог бы привести настройки, при которых вторая копия не запустится?

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

Позволь только спросить - у тебя на скольких машинах отключен overcommit?

Гениальный вопрос. На двух

Работа требует или ради интереса?

Или ты тренируешься в телепатии?

Мне любопытно, откуда взялось твое понимание смысла отключения overcommit.

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

в первом сообщении топика настройки приведены

То есть overcommit отключен? Тогда вторая копия и не должна запускаться.

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

Работа требует или ради интереса?

Ради стабильности.

Мне любопытно, откуда взялось твое понимание смысла отключения overcommit.

Из документации ядра. Хотя ты, разумеется, волен черпать это понимание из других источников: магия, гадания, «я так вижу»...

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

Ога, там есть только проверка на выделять/не-выделять никакого выделения физической памяти при отключенном overcommit'е не вижу.

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

Мне любопытно, откуда взялось твое понимание смысла отключения overcommit.

Из документации ядра

Такие фразы всегда напоминали мне про «смерть от опечатки».

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

А я разве говорил, что это нештатное поведение?

А я разве говорил, что ты это говорил? %)

tailgunner ★★★★★
()

В общем Reset, похоже, прав.

$ cat /usr/src/linux/Documentation/filesystems/proc.txt
...
 CommitLimit: Based on the overcommit ratio ('vm.overcommit_ratio'),
              this is the total amount of  memory currently available to
              be allocated on the system. This limit is only adhered to
              if strict overcommit accounting is enabled (mode 2 in
              'vm.overcommit_memory').
              The CommitLimit is calculated with the following formula:
              CommitLimit = ('vm.overcommit_ratio' * Physical RAM) + Swap
              For example, on a system with 1G of physical RAM and 7G
              of swap with a `vm.overcommit_ratio` of 30 it would
              yield a CommitLimit of 7.3G.
              For more details, see the memory overcommit documentation
              in vm/overcommit-accounting.
Committed_AS: The amount of memory presently allocated on the system.
              The committed memory is a sum of all of the memory which
              has been allocated by processes, even if it has not been
              "used" by them as of yet. A process which malloc()'s 1G
              of memory, but only touches 300M of it will only show up
              as using 300M of memory even if it has the address space
              allocated for the entire 1G. This 1G is memory which has
              been "committed" to by the VM and can be used at any time
              by the allocating application. With strict overcommit
              enabled on the system (mode 2 in 'vm.overcommit_memory'),
              allocations which would exceed the CommitLimit (detailed
              above) will not be permitted. This is useful if one needs
              to guarantee that processes will not fail due to lack of
              memory once that memory has been successfully allocated.
...
И раньше ещё был CommitAvail http://lkml.org/lkml/2004/10/24/118

Тогда надо или ставить overcommit_ratio больше 100-а % или включать своп.

А зачем вообще нужен этот optimistic mallocating?

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

IO waiting buffers это буфера, слайсы, куда будут попадать, например, входящие сетевые данные. Так же это кеш файловой системы.

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

Да. Причем виртуальная память выделяется по странично и сумируются целые страницы. Но в виртуальную память так же попадают библиотеки и прочие файлы которые живут на диске. Вот они не учитываются при расчете свободной памяти, но они попадают в overcommit_ratio и если его выкрутить, то у тебя просто дико выростет IO wait.

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

И словишь ты недетские тормоза при запуске программ или обращению к какому-то коду.

Понимаешь, в чем дело, shared library, сам код программы, тоже лежит в виртуальной памяти и т.к. эти страницы подняты с диска, то они будут опускаться (вытесняться). Т.е. технически линуксу хватит одной странице в 4 килобайта что бы все жило. Но скорость работы будет, ой.

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

Это поведение зависит от настройки системы, так, к слову.

Повторил мои слова. А что сказать-то хотел?

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

Но в виртуальную память так же попадают библиотеки и прочие файлы которые живут на диске.

Да, но они учитываются в кэше. И первая строка во free показывает _полностью_ свободную память. Или нет?

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

да, но это всё принадлежит дисковому кэшу и буферам.

Теперь смотри пример выше Как выделяется память в линуксе? (комментарий) :
$ sudo free
total used free shared buffers cached
Mem: 2072164 945072 1127092 0 33472 234712
-/+ buffers/cache: 676888 1395276
Swap: 0 0 0
$ sudo ./malloctest
...
total allocated 1051321344

1127092к - 1051321344 = ~100Mb памяти свободной осталось. ~100Мб отрезано overcommit_ratio (2Г * .05). А там ещё 266Мб кэшей, которые должны были бы вытеснится. Ну пусть не все 266, но метров 200 ещё можно было бы выделить.

Вот тут http://www.win.tue.nl/~aeb/linux/lk/lk-9.html в параграфе 9.6 чувак пишет: «Linux on the other hand is seriously broken. It will by default answer „yes“ to most requests for memory, in the hope that programs ask for more than they actually need.» Т.е. для меня это выглядит так, что какая(-ие)-то программа аллоцировала больше памяти, чем ей на самом деле нужно. Если теперь выставить overcommit_ratio в процентов 105-110, то как раз вся память будет использована.

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