Надеюсь, теперь вы убедились, что Linux не “съел” всю вашу оперативную память. Вот несколько интересных вещей, которые вы можете сделать, чтобы узнать, как работает дисковый кэш.

Примечание: Приведенные примеры относятся к оборудованию 2009 года без SSD. Возможно, вам придется умножить несколько приведенных здесь чисел на 10, чтобы оценить реальный эффект.

Влияние дискового кэша на аллокацию памяти приложениями

Поскольку я уже заверил, что дисковый кэш не мешает приложениям получать нужную им память, давайте с этого и начнем. Вот приложение на языке Си (munch.c), которое поглотит столько памяти, сколько сможет, или до заданного предела:

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

int main(int argc, char** argv) {
    int max = -1;
    int mb = 0;
    char* buffer;

    if(argc > 1)
        max = atoi(argv[1]);

    while((buffer=malloc(1024*1024)) != NULL && mb != max) {
        memset(buffer, 0, 1024*1024);
        mb++;
        printf("Allocated %d MB\n", mb);
    }

    return 0;
}

Закончившаяся память — это не очень весело, но механизм OOM (Out-Of-Memory) killer должен завершить только этот процесс и, надеюсь, остальные останутся нетронутыми. Мы определенно захотим отключить своп в данном случае, иначе приложение поглотит и его.

$ sudo swapoff -a

$ free -m

(обратите внимание, что ваш вывод free может быть другим, и иметь столбец 'available' (доступная) вместо строки '-/+')

             total       used       free     shared    buffers     cached
Mem:          1504       1490         14          0         24        809
-/+ buffers/cache:        656        848
Swap:            0          0          0

$ gcc munch.c -o munch

$ ./munch
Allocated 1 MB
Allocated 2 MB
(...)
Allocated 877 MB
Allocated 878 MB
Allocated 879 MB
Killed

$ free -m

             total       used       free     shared    buffers     cached
Mem:          1504        650        854          0          1         67
-/+ buffers/cache:        581        923
Swap:            0          0          0

Несмотря на то, что было указано 14 МБ "free" (свободная), это не помешало приложению захватить 879 МБ. После этого кэш практически пуст[2], но постепенно он снова заполнится по мере чтения и записи файлов. Попробуйте.

Влияние дискового кэша на свопинг

Я также сказал, что кеш диска не заставит приложения использовать своп (подкачку). Давайте попробуем и это, с тем же приложением munch, что и в предыдущем эксперименте. На этот раз мы запустим его с включенным свопом и ограничимся несколькими сотнями мегабайт:

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504       1490         14          0         10        874
-/+ buffers/cache:        605        899
Swap:         2047          6       2041

$ ./munch 400
Allocated 1 MB
Allocated 2 MB
(...)
Allocated 399 MB
Allocated 400 MB

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504       1090        414          0          5        485
-/+ buffers/cache:        598        906
Swap:         2047          6       2041

munch “съел” 400MB оперативной памяти, которая была взята из дискового кэша, не обращаясь к свопу. Аналогично, мы можем снова заполнить дисковый кэш, и он также не станет использовать своп. Если в одном терминале запустить watch free -m и find . -type f -exec cat {} + > /dev/null в другом, вы увидите, что "cached" (кэшированная) память будет расти, а "free" падать. Через некоторое время это замедляется, но своп так и остается нетронутым[1].

Очистка дискового кэша

При проведении экспериментов очень удобно иметь возможность сбросить дисковый кэш. Для этого мы можем использовать специальный файл /proc/sys/vm/drop_caches. Записав в него значение 3, мы можем очистить большую часть дискового кэша:

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504       1471         33          0         36        801
-/+ buffers/cache:        633        871
Swap:         2047          6       2041

$ echo 3 | sudo tee /proc/sys/vm/drop_caches 
3

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504        763        741          0          0        134
-/+ buffers/cache:        629        875
Swap:         2047          6       2041

Обратите внимание, что "buffers" (буфера. буферная память) и "cached" уменьшились, free mem (свободная память) увеличилась, а free+buffers/cache остались прежними.

Влияние дискового кэша на время загрузки

Давайте сделаем две тестовые программы, одну на Python, а другую на Java. И Python, и Java имеют довольно большие рантаймы, которые должны быть загружены для запуска приложения. Это идеальный сценарий для работы дискового кэша.

$ cat hello.py
print "Hello World! Love, Python"

$ cat Hello.java
class Hello {
    public static void main(String[] args) throws Exception {
        System.out.println("Hello World! Regards, Java");
    }
}

$ javac Hello.java

$ python hello.py
Hello World! Love, Python

$ java Hello
Hello World! Regards, Java

Наши приложения “hello world” работают. Теперь давайте сбросим дисковый кэш и посмотрим, сколько времени потребуется для их запуска.

$ echo 3 | sudo tee /proc/sys/vm/drop_caches
3

$ time python hello.py
Hello World! Love, Python

real    0m1.026s
user    0m0.020s
sys     0m0.020s

$ time java Hello
Hello World! Regards, Java

real    0m2.174s
user    0m0.100s
sys     0m0.056s

$

Ого. 1 секунда для Python и 2 секунды для Java? Это много, чтобы просто сказать "привет" (hello). Однако теперь все файлы, необходимые для их запуска, будут находиться в дисковом кэше, поэтому их можно будет получить прямо из памяти. Давайте попробуем еще раз:

$ time python hello.py
Hello World! Love, Python

real    0m0.022s
user    0m0.016s
sys     0m0.008s

$ time java Hello
Hello World! Regards, Java

real    0m0.139s
user    0m0.060s
sys     0m0.028s

$

Ура! Теперь Python запускается всего за 22 миллисекунды, в то время как java использует 139 мс. Это в 45 и 15 раз быстрее! Все ваши приложения получают этот буст автоматически!

Влияние дискового кэша на чтение файлов

Давайте создадим большой файл и посмотрим, как дисковый кэш влияет на скорость его считывания. Я делаю файл размером 200 МБ, но если у вас меньше свободной памяти, то можете изменить это значение.

$ echo 3 | sudo tee /proc/sys/vm/drop_caches
3

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504        546        958          0          0         85
-/+ buffers/cache:        461       1043
Swap:         2047          6       2041

$ dd if=/dev/zero of=bigfile bs=1M count=200
200+0 records in
200+0 records out
209715200 bytes (210 MB) copied, 6.66191 s, 31.5 MB/s

$ ls -lh bigfile
-rw-r--r-- 1 vidar vidar 200M 2009-04-25 12:30 bigfile

$ free -m
             total       used       free     shared    buffers     cached
Mem:          1504        753        750          0          0        285
-/+ buffers/cache:        468       1036
Swap:         2047          6       2041

Поскольку файл был только что записан, он отправится в дисковый кэш. Файл размером 200 МБ привел к увеличению "cached" на 200 МБ. Давайте его прочтем, очистим кэш и прочитаем снова, чтобы посмотреть, насколько это быстро:

$ time cat bigfile > /dev/null

real    0m0.139s
user    0m0.008s
sys     0m0.128s

$ echo 3 | sudo tee /proc/sys/vm/drop_caches
3

$ time cat bigfile > /dev/null

real    0m8.688s
user    0m0.020s
sys     0m0.336s

$

Это более чем в пятьдесят раз быстрее!

Выводы

Дисковый кэш Linux очень ненавязчив. Он использует резервную память для значительного увеличения скорости доступа к диску, при этом не отнимая памяти у приложений. Полностью использованная память в Linux — это эффективное применение оборудования, а не сигнал тревоги.


В данном материале есть немного полезной информации:

  1. Хотя вновь аллоцированная память всегда (впрочем, см. пункт #2) будет браться из дискового кэша вместо свопа, Linux может быть сконфигурирован для предварительной выгрузки других неиспользуемых приложений в фоновом режиме, чтобы освободить память для кэша. Это настраивается с помощью параметра swappiness, доступного через /proc/sys/vm/swappiness.

    Сервер может захотеть выгрузить неиспользуемые приложения, чтобы ускорить доступ к диску для работающих программ ("сделать систему быстрее"), в то время как десктопная система будет держать их в памяти, чтобы предотвратить задержку, когда пользователь наконец воспользуется ими ("сделать систему более отзывчивой"). Данная тема вызывает много споров.

  2. Некоторые части кэша не могут быть сброшены, даже для размещения новых приложений. К ним относятся mmap-страницы, которые были заблокированы системным вызовом mlock какого-либо приложения, "грязные" страницы (dirty pages), пока не записанные в память, и данные, хранящиеся в tmpfs (включая /dev/shm, используемую для общей памяти). Заблокированные (mlocked) mmap-страницы застревают в кэше страниц. "Грязные" страницы в большинстве случаев будут быстро переписаны. Данные в tmpfs по-возможности будут выгружены.


На днях пройдет открытый урок «LVM для начинающих», на котором разберем основные моменты использования LVM для организации дисковой системы в Linux. На занятии узнаем, что такое LVM и когда он нужен, что в него входит (PV, VG, LV). А также научимся, что можно сделать с его использованием. Регистрация открыта по ссылке.

Комментарии (7)


  1. mpa4b
    20.12.2022 20:53
    -1

    специальный файл /proc/sys/vm/drop_caches

    Очищаю дисковые кеши для 2 случаев:

    1. когда проверяю sd-карты на вшивость -- например вычитать всю карту 2 раза и сравнить хеши. Очевидно, что нужно очищать кеш, иначе второй раз вычитается именно он.

    2. Когда проверяю что данные типа бекапов правильно записались на диск, хотя это уже попахивает параноей, особенно если бекап больше кол-ва ОЗУ :)

    Для упрощения жизни патчу ядро, чтобы этот псевдо-файл был с пермишенами 0222 (т.е. доступный на запись для всех).


    1. ktotomskru
      21.12.2022 02:50
      +2

      ну нифига себе вы упростили жизнь, вместо использования sudo/suid или чего-то такого


      1. mpa4b
        21.12.2022 16:37
        -1

        Именно! Вводить каждый раз 15-буквенный пароль влом :)


    1. Sap_ru
      21.12.2022 03:08
      +2

      Вашу задачу правильнее решать через dd с ключами для чтения в обход кэша. Сброс кэша сбрасывает не всё и не всегда.
      "dd iflag=direct", если не ошибаюсь. В идеале нужно бы ещё размер блока выставить заведомо больше чем, блок флэша, но кратным ему. Обычно подходят значения 64кБ, 128 кБ, либо 4МБ - в зависимости от типа накопителя. 4МБ гарантировано решает задачу даже на шаманских новых SSD-дисках с очень большими физическими блоками секторов.


      1. mpa4b
        21.12.2022 16:39
        -1

        Спасибо. Я конечно попробую ещё раз, но по воспоминаниям, такой флаг приводил к более медленной работе.

        А ещё например, с кешем можно одновременно сливать диск на ссд и считать хешсумму, в этом случае 2 программы сначала догоняют друг друга, потом упираются в один и тот же медленный диск, получая блоки либо из блочного кеша, либо свежесчитанные.


        1. Sap_ru
          21.12.2022 16:55
          +1

          Естественно, к более медленной - буфера-то не используются. Но вы именно этого и хотели, чтобы физический носитель проверить. Медленная работу можно компенсировать увеличением объёма буфера, но так, чтобы он оставался кратен физическому сектору. В общем 4МБ буфер и прочие бОльшие степени двойки отлично решат проблему низкой скорости настолько, насколько это возможно без использования буферов чтения.
          С двумя программами может некрасиво получиться. Во-первых гонки внутри драйверов скорость могут снижать скорость, а во-вторых, на больших объёмах они могут отлично разойтись на объём, больший чем размер кэша, и тогда всё начнёт тормозить.


          1. mpa4b
            21.12.2022 21:29

            Проверил по-быстрому на простаивающем SBC с sdкартой -- возражение снимаю, на мегабайтных `bs=` получается по скорости как и без iflag=direct и чистыми кешами.

            Второй случай естественно полезен только для ситуации, когда каждая из программ пережёвывает информацию сильно быстрее, чем диск её может выдавать.