Амнезия FreeBSD

Я никогда не понимал как работает распределение памяти во FreeBSD. Из всего многообразия документации полезное помнилось, лишь

An urban myth has circulated for years that Linux did a better job avoiding swapouts than FreeBSD, but this in fact is not true. What was actually occurring was that FreeBSD was proactively paging out unused pages in order to make room for more disk cache while Linux was keeping unused pages in core and leaving less memory available for cache and process pages.


20be2314990f8cdf3009281f2911880d.png
Ну лучше чем Linux, да и пусть. Я не против. Но хуже самого непонимая процесса выделения памяти меня убивала Inactive память. Что это такое и можно ли «это» безболезненно использовать? Считать ли эту память доступной для использования приложением?

Под cut’ом больше вопросов чем ответов.
FAQ FreeBSD сообщает, что

16.2.

Why does top show very little free memory even when I have very few programs running?

The simple answer is that free memory is wasted memory. Any memory that programs do not actively allocate is used within the FreeBSD kernel as disk cache. The values shown by top (1) labeled as Inact, Cache, and Buf are all cached data at different aging levels. This cached data means the system does not have to access a slow disk again for data it has accessed recently, thus increasing overall performance. In general, a low value shown for Free memory in top (1) is good, provided it is not very low.

Хорошо, пусть это кеш какого-то уровня, но почему не поместить эту Inact память в Cache? Может быть потому, что она доступна для использования (как утверждают многочисленные форумчане) и пусть не моментально, но может быть выделена по запросу?

Попытаемся выяснить это практическим путём. Имеем:

# top -b 0
last pid:  1019;  load averages:  0.21,  0.45,  0.24  up 0+00:03:33    14:26:30
28 processes:  1 running, 27 sleeping

Mem: 18M Active, 17M Inact, 130M Wired, 24M Buf, 3756M Free
Swap: 3852M Total, 3852M Free


То есть почти вся память Free и своп полностью свободен.
Теперь, чтобы задействовать доступную память, создадим tmpfs раздел.

# mkdir /tmp/gb
# mount -t tmpfs -o mode=01777,size=3221225472 tmpfs /tmp/gb
# df -h | egrep "(Filesystem|tmpfs)"
Filesystem     Size    Used   Avail Capacity  Mounted on
tmpfs          3.0G    4.0K    3.0G     0%    /tmp/gb

При этом

# top -b 0
last pid:  1028;  load averages:  0.09,  0.19,  0.17  up 0+00:09:30    14:32:27
28 processes:  1 running, 27 sleeping

Mem: 18M Active, 17M Inact, 130M Wired, 24M Buf, 3756M Free
Swap: 3852M Total, 3852M Free


Согласен, раз раздел просто создан/примонтирован, но свободен, то незачем выделять ему память.
Поместим в него файл.

# dd if=/dev/urandom of=/tmp/gb/file.txt bs=1M count=3k
3072+0 records in
3071+0 records out
3220176896 bytes transferred in 53.334672 secs (60376801 bytes/sec)
# df -h | egrep "(Filesystem|tmpfs)"
Filesystem     Size    Used   Avail Capacity  Mounted on
tmpfs          3.0G    3.0G    1.0M   100%    /tmp/gb


3 гигабайта памяти занято, но при этом

 # top -b 0
last pid:  1040;  load averages:  0.19,  0.26,  0.20  up 0+00:16:40    14:39:37
28 processes:  1 running, 27 sleeping

Mem: 18M Active, 3088M Inact, 137M Wired, 24M Buf, 677M Free
Swap: 3852M Total, 3852M Free


они почему-то считаются Inact. Но раз она не Active, то попытаемся её задествовать. Набросаем небольшой «Hello, world!» для выделения памяти и её последующего освобождения:

#include 
#include 
#include 
#include 
int main (int argc, char *argv[])
{
        int i;
        char *buffer[64];
        long lSize = 1024*1024*1024;
        int iMB = argc < 2 ? 1 : atoi(argv[1]);
        printf("iMB:\t%d\n", iMB);

        for(i=0; i < iMB; i++)
        {
                buffer[i] = (char*) malloc (lSize);
                if(buffer[i] != NULL)
                {
                        printf("Alloc: %d\n", i);
                        memset(buffer[i], 127, lSize);
                } else printf("Error!\n");
                sleep(1);
        }
        sleep(10);
        for(i=0; i < iMB; i++)
        {
                printf("Free: %d\n", i);
                free (buffer[i]);
        }
        return 0;
}

# time ./a.out 3
iMB:    3
Alloc: 0
Alloc: 1
Alloc: 2
Free: 0
Free: 1
Free: 2
0.915u 1.475s 1:00.16 3.9%      5+168k 0+0io 0pf+0w


Почему так долго? Вероятно наш tmpfs, якобы находящийся в Inactive, выдавливался в своп.

Дествительно, при выделении памяти (момент «sleep (10);» в коде) видим:

# top -b 0
last pid:  1128;  load averages:  0.02,  0.11,  0.14  up 0+00:28:34    14:51:31
37 processes:  1 running, 36 sleeping

Mem: 3106M Active, 621M Inact, 155M Wired, 26M Cache, 27M Buf, 14M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse


Но хуже другое. После освобождения памяти приложением:

# top -b 0
last pid:  1129;  load averages:  0.09,  0.12,  0.15  up 0+00:28:48    14:51:45
36 processes:  1 running, 35 sleeping

Mem: 33M Active, 621M Inact, 145M Wired, 26M Cache, 27M Buf, 3095M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse


своп остался задействован.
Обращения к файлу вновь вернули память в Inact

# time dd of=/dev/zero if=/tmp/gb/file.txt bs=1M count=3k
3071+0 records in
3071+0 records out
3220176896 bytes transferred in 40.265654 secs (79973292 bytes/sec)
0.008u 3.796s 0:40.26 9.4%      22+154k 0+0io 0pf+0w
# time dd of=/dev/zero if=/tmp/gb/file.txt bs=1M count=3k
3071+0 records in
3071+0 records out
3220176896 bytes transferred in 1.242623 secs (2591434941 bytes/sec)
0.000u 1.241s 0:01.24 100.0%    25+173k 0+0io 0pf+0w
# top -b 0
last pid:  1144;  load averages:  0.09,  0.12,  0.14  up 0+00:36:22    14:59:19
36 processes:  1 running, 35 sleeping

Mem: 29M Active, 3077M Inact, 146M Wired, 4K Cache, 27M Buf, 669M Free
Swap: 3852M Total, 2502M Used, 1350M Free, 64% Inuse


При этом непонятно почему остался Swap: 2502M Used

Но, допустим, это нормально и при наличии свободного свопа можно считать эту задействанную память неактивной и помечать её как Inact. Что же будет, если у нас нет свопа? Надеюсь, теперь задействованная память теперь будет Active.
Убираем своп, аналогично монтируем 3Gb раздел tmpfs и заполняем его.

# top -b 0
last pid:  1013;  load averages:  0.58,  0.53,  0.29  up 0+00:05:03    15:11:46
34 processes:  1 running, 33 sleeping

Mem: 21M Active, 3089M Inact, 138M Wired, 24M Buf, 673M Free
Swap:


Для очистки совести убедимся что top не врёт:

# expr `sysctl -n vm.stats.vm.v_inactive_count` \* `sysctl -n vm.stats.vm.v_page_size`
3239620608


И, несмотря на отсутствие свопа, наша занятая память по прежнему не совсем активна… Раз она не активна попробуем её вновь занять нашим приложением.

# ./a.out 3
iMB:    3
Alloc: 0
Killed


Ч.т.д. и, наконец:

# top -b 0
last pid:  1026;  load averages:  0.15,  0.22,  0.21  up 0+00:11:37    15:18:20
34 processes:  1 running, 33 sleeping

Mem: 3102M Active, 1524K Inact, 138M Wired, 200K Cache, 24M Buf, 679M Free
Swap:

# expr `sysctl -n vm.stats.vm.v_inactive_count` \* `sysctl -n vm.stats.vm.v_page_size`
1720320

Здесь, наверное, должен быть какой-то вывод, но его нет…

© Habrahabr.ru