Эксперименты и увлекательные манипуляции с дисковым кэшем Linux
Надеюсь, теперь вы убедились, что 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 — это эффективное применение оборудования, а не сигнал тревоги.
В данном материале есть немного полезной информации:
Хотя вновь аллоцированная память всегда (впрочем, см. пункт #2) будет браться из дискового кэша вместо свопа, Linux может быть сконфигурирован для предварительной выгрузки других неиспользуемых приложений в фоновом режиме, чтобы освободить память для кэша. Это настраивается с помощью параметра
swappiness
, доступного через/proc/sys/vm/swappiness
.Сервер может захотеть выгрузить неиспользуемые приложения, чтобы ускорить доступ к диску для работающих программ ("сделать систему быстрее"), в то время как десктопная система будет держать их в памяти, чтобы предотвратить задержку, когда пользователь наконец воспользуется ими ("сделать систему более отзывчивой"). Данная тема вызывает много споров.
Некоторые части кэша не могут быть сброшены, даже для размещения новых приложений. К ним относятся mmap-страницы, которые были заблокированы системным вызовом
mlock
какого-либо приложения, "грязные" страницы (dirty pages), пока не записанные в память, и данные, хранящиеся в tmpfs (включая/dev/shm
, используемую для общей памяти). Заблокированные (mlocked) mmap-страницы застревают в кэше страниц. "Грязные" страницы в большинстве случаев будут быстро переписаны. Данные вtmpfs
по-возможности будут выгружены.
На днях пройдет открытый урок «LVM для начинающих», на котором разберем основные моменты использования LVM для организации дисковой системы в Linux. На занятии узнаем, что такое LVM и когда он нужен, что в него входит (PV, VG, LV). А также научимся, что можно сделать с его использованием. Регистрация открыта по ссылке.