- How to clear the buffer/pagecache (disk cache) under Linux
- What is Memory Cache
- What is the purpose of /proc/sys/vm/drop_caches
- How to clear the Memory Cache using /proc/sys/vm/drop_caches
- How to clear the Memory Cache using sysctl
- Linux Page Cache для SRE: основные файловые операции и syscall’ы (часть 1)
- Подготовка окружения
- Чтение файлов и Page Cache
- Чтение файлов используя read() syscall
- Чтение файлов с помощью системного вызова mmap()
- Запись в файл и Page Cache
- Запись в файлы с помощью системного вызова write()
- Запись в файл с помощью mmap() syscall
- Грязные страницы кеша (dirty pages)
- Синхронизация данных файла: fsync(), fdatasync() и msync()
- Проверяем наличие данных файла в Page Cache с помощью mincore()
- Вывод
How to clear the buffer/pagecache (disk cache) under Linux
Are you facing a performance issue and you suspect it might be related to cache usage? High cache usage should not normally cause performance issues, but it might be the root cause in some rare cases.
What is Memory Cache
In order to speed operations and reduce disk I/O, the kernel usually does as much caching as it has memory By design, pages containing cached data can be repurposed on-demand for other uses (e.g., apps) Repurposing memory for use in this way is no slower than claiming pristine untouched pages.
What is the purpose of /proc/sys/vm/drop_caches
Writing to /proc/sys/vm/drop_caches allows one to request the kernel immediately drop as much clean cached data as possible. This will usually result in some memory becoming more obviously available; however, under normal circumstances, this should not be necessary.
How to clear the Memory Cache using /proc/sys/vm/drop_caches
Writing the appropriate value to the file /proc/sys/vm/drop_caches causes the kernel to drop clean caches, dentries and inodes from memory, causing that memory to become free.
1. In order to clear PageCache only run:
2. In order to clear dentries (Also called as Directory Cache) and inodes run:
3. In order to clear PageCache, dentries and inodes run:
Running sync writes out dirty pages to disks. Normally dirty pages are the memory in use, so they are not available for freeing. So, running sync can help the ensuing drop operations to free more memory.
Page cache is memory held after reading files. Linux kernel prefers to keep unused page cache assuming files being read once will most likely to be read again in the near future, hence avoiding the performance impact on disk IO.
dentry and inode_cache are memory held after reading directory/file attributes, such as open() and stat(). dentry is common across all file systems, but inode_cache is on a per-file-system basis. Linux kernel prefers to keep this information assuming it will be needed again in the near future, hence avoiding disk IO.
How to clear the Memory Cache using sysctl
You can also Trigger cache-dropping by using sysctl -w vm.drop_caches=[number] command.
1. To free pagecache, dentries and inodes, use the below command.
2. To free dentries and inodes only, use the below command.
3. To free the pagecache only, use the below command.
Источник
Linux Page Cache для SRE: основные файловые операции и syscall’ы (часть 1)
В этой серии постов я хотел бы поговорить о Linux Page Cache. Я считаю, что данные знания теории и инструментов жизненно необходимы и важны для каждого SRE. Общее понимание как работает Page Cache помогает и в рутинных повседневных задачах, и в экстренной отладке на продакшене. При этом Page Cache часто оставляют без внимания, а ведь его лучшее понимание. как правило, приводит к:
более точному планированию емкости системы и лимитов сервисов и контейнеров;
улучшенным навыкам отладки приложений, интенсивно использующих память и диски (СУБД и хранилища данных);
созданию безопасных и предсказуемых сред выполнения специальных задач, связанных с памятью и/или вводом-выводом (например: сценарии резервного копирования и восстановления, rsync однострочники и т.д.).
Я покажу, какие утилиты вы можете использовать, когда имеете дело с задачами и проблемами, связанными с Page Cache, как правильно к ним подходить, и как понять реальное использование памяти.
Подготовка окружения
Начнём с подготовки окружения. Нам понадобиться файл для тестов:
И утилита vmtouch . На арче (BTW, I use Arch Linux) её легко поставить из aur ‘a:
И сбрасываем все кеши, чтобы получить чистую систему:
Теперь пришло время засучить рукава и приступить к практическим примерам.
На данный момент мы игнорируем как работает утилита vmtouch . Позже в этой статье я покажу, как написать его альтернативу с практически всеми фичами.
Чтение файлов и Page Cache
Чтение файлов используя read() syscall
Начнём с простой программы, которая считывает первые 2 байта из нашего тестового файла /var/tmp/file1.db .
Обычно такого рода запросы транслируются в системный вызов read() . Давайте запустим скрипт под strace , для того чтобы убедиться что f.read() действительно вызывает системный вызов read() :
Результат должен выглядеть как-то так:
Из вывода видно, что системный вызов read() возвращает 4096 байт (одна страница) не смотря на то, что наш скрипт запрашивал только 2 байта. Это пример того, как python оптимизирует работу буферизованного ввода-вывода. Хотя это и выходит за рамки данного поста, но в некоторых случаях важно иметь это в виду.
Теперь давайте проверим, сколько данных закэшировало ядро. Для получения этой информации мы используем vmtouch :
Из вывода мы видим, что вместо 2B данных, которые запрашивал python, ядро закэшировало 80 КБ или 20 страниц.
Ядро линукс в принципе не может загружать в Page Cache ничего меньше 4 КБ или одной страницы. Но почему их там оказалось на 19 страниц больше? Это отличный пример того, как ядро использует опережающее чтение (readahead) и предпочитает выполнять последовательные операции ввода-вывода, а не случайные. Основная идея состоит в том, чтобы предсказать последующие чтения и свести к минимуму количество запросов к диску. Этим поведением можно управлять с помощью системных вызовов:
posix_fadvise() ( man 2 posix_fadvise ) и
readahead() ( man 2 readahead ).
Обычно, в продакшене для систем управления базами данных и дисковых хранилищ, не имеет большого смысла в настройках параметров опережающего чтения. Если СУБД не нужны данные, которые были кэшированы при опережающем чтении, политика восстановления памяти ядра (memory reclaim) должна в конечном итоге удалить эти страницы из Page Cache. Так же, как правило, последовательный ввод-вывод не является дорогостоящим для ядра и аппаратного обеспечения. В свою очередь отключение опережающего чтения вообще – может даже навредить и привести к некоторому снижению производительности из-за увеличения числа операций ввода-вывода в дисковых очередях ядра, бÓльшего количества переключений контекста (context switches) и бÓльшего времени для подсистемы управления памятью ядра для распознавания рабочего набора данных (working set). Мы поговорим о политике восстановления памяти (memory reclaim), нагрузке на память (memory pressure) и обратной записи в кэш (writeback) позже в этой серии постов.
Теперь давайте посмотрим как использование posix_fadvise() может уведомить ядро о том, что мы читаем файл случайным образом, и поэтому не хотим иметь никакого опережающего чтения (readahead):
Перед запуском скрипта нам нужно сбросить все кэши:
Теперь, если вы проверите выдачу vmtouch – вы можете увидеть, что, как и ожидалось, там находится лишь одна страница:
Чтение файлов с помощью системного вызова mmap()
Для чтение данных из файла мы также можем использовать системный вызов mmap() ( man 2 mmap ). mmap() является “волшебным” инструментом и может быть использован для решения широкого круга задач. Однако для наших тестов нам понадобиться только одна его особенность, а именно, возможность отображать файл на адресное пространство процесса. Это позволяет получить доступ к файлу в виде плоского массива. Я расскажу детально о mmap() далее в этом цикле статей. Но сейчас, если вы совсем не знакомы с mmap() , его API должен быть понятным из следующего примера:
Данный код делает то же самое, что и системный вызов read() . Он читает первые 2 байта из файла.
Также в целях тестирования нам необходимо очистить кэш перед выполнением скрипта:
Теперь давайте посмотрим на содержимое Page Cache:
Как вы видите mmap() выполнил еще более агрессивный readahead, чем read() .
Давайте теперь изменим readahead при помощи системного вызва madvise() как это было сделано с fadvise() .
и содержимое Page Cache :
Как вы можете видеть с MADV_RANDOM нам удалось загрузить ровно одну страницу в Page Cache.
Запись в файл и Page Cache
Теперь давайте поэкспериментируем с записью.
Запись в файлы с помощью системного вызова write()
Давайте продолжим работу с нашим экспериментальным файлом и попробуем записать первые 2 байта:
Будьте осторожны и не открывайте файл в режиме w . Он перезапишет ваш файл и сделает его размером в 2 байта. Нам нужен режим r+ .
Удалите все из Page Cache и запустите приведенный выше скрипт:
Теперь давайте проверим содержимое Page Cache.
Как вы можете видеть, в Page Cache находится 1 страница данных. Это достаточно важное наблюдение, так как, если происходят записи размером меньше размера страницы, то им будут предшествовать 4 Кб чтения, для того, чтобы загрузить данные в Page Cache.
Также мы можем проверить состояние грязных (dirty) страниц, заглянув в файл статистики памяти cgroup.
Получаем текущую cgroup:
Запись в файл с помощью mmap() syscall
Давайте теперь повторим наш опыт с записью но будем использовать в этот раз mmap() :
Вы можете самостоятельно повторить вышеописанные команды vmtouch , cgroup и grep . В итоге вы должны получить тот же результат. Единственным исключением является опережающее чтение. По умолчанию mmap() загружает гораздо больше данных в кэш страниц даже при записи.
Грязные страницы кеша (dirty pages)
Как мы видели ранее, процесс генерирует грязные страницы путем записи в файлы через кэш.
Linux предоставляет несколько вариантов получения количества грязных (dirty) страниц. Первый и самый старый из них – это прочитать системный файл /proc/memstat :
Часто такую системную информацию трудно интерпретировать и использовать, поскольку мы не можем точно определить, какой процесс их сгенерировал и к какому файлу они относятся.
Поэтому, как было показано выше, лучшим вариантом для получения данной информации лучше всего использовать cgroup:
Если же ваша программа использует mmap() для записи в файлы, у вас есть еще один вариант получения статистики с детализацией по каждому процессу. В procfs есть специальный файл для каждого процесса /proc/PID/smap , где отображаются счетчики памяти с разбивкой по областям виртуальной памяти (VMA). Как мы помним, с помощью mmap() процесс отображает файл на свою память, что следовательно создает VMA с файлом и соответствующей информацией. Мы можем получить грязные страницы, найдя там:
Private_Dirty – объем грязных данных, сгенерированных этим процессом;
Shared_Dirty – грязные страницы других процессов. Эта метрика будет отображать данные только для страниц, на которые есть ссылки (referenced memory). Это означает, что процесс должен был обратиться к этим страницам раньше и сохранить их в своей таблице страниц (page table) (подробнее позже).
Но что, если мы хотим получить статистику наличия грязных страниц (dirty pages) для конкретного файла. Чтобы ответить на этот вопрос, ядро Linux предоставляет 2 файла в procfs : /proc/PID/pagemap и /proc/kpageflags . Я покажу, как используя эти файлы написать наш собственный инструмент позже в этом цикле статей, а сейчас мы можем использовать инструмент отладки памяти из репозитория ядра Linux чтобы получить информацию о страницах файла: page-types (https://github.com/torvalds/linux/blob/master/tools/vm/page-types.c).
Я отфильтровал все страницы нашего файла /var/tmp/file1.db по наличию грязного ( dirty ) флага. В выводе вы можете видеть, что файл содержит 287 грязных страниц или 1 МБ грязных данных, которые в конечном итоге будут записаны обратно в хранилище. page-type объединяет страницы по флагам, поэтому в выводе вы можете увидеть 2 набора страниц. У обоих есть грязный флаг D , и разница между ними заключается в наличии флага R .
Синхронизация данных файла: fsync(), fdatasync() и msync()
Мы уже использовали команду для синхронизации ( man 1 sync ) всех грязных страниц системы на диски перед каждым тестом, для того чтобы получить свежий Page Cache. Но что делать, если мы хотим написать систему управления базами данных, и нам нужно быть уверенными, что все записи попадут на диски до того, как произойдет отключение питания или другие аппаратные ошибки. Для таких случаев linux предоставляет несколько способов заставить ядро совершить сброс грязных страницы для конкретного файла из Page Cache на диски:
fsync() – блокируется до тех пор, пока не будут синхронизированы все грязные страницы конкретного файла и его метаданные;
fdatasync() – то же самое, но без метаданных;
msync() – то же самое, что делает fsync() , но для файла, отображенного на память процесса;
флагами открытия файла: O_SYNC или O_DSYNC сделают все записи в файл синхронными по умолчанию.
Вам все еще нужно заботиться о барьерах записи (write barriers) и понимать, как работает ваша файловая система. Обычно операции добавления в конец файла безопасны и не могут повредить данные которые были записаны до этого Другие же типы операций записи могут повредить ваши файлы (например, даже с настройками журнала по умолчанию в ext4). Поэтому все системы управления базами данных, такие как MongoDB, PostgreSQL, Etcd, Dgraph и т. д., используют журналы предварительной записи (WAL). Если вам интересно узнать более подробнее об этой теме, – можно пожалуй начать с поста в блоге Dgraph.
А вот и пример синхронизации файлов:
Проверяем наличие данных файла в Page Cache с помощью mincore()
Настало время выяснить, каким же таким способом vmtouch удается показать нам, сколько страниц того или иного файла содержит Page Cache.
Секрет заключается в системном вызове mincore() ( man 2 mincore ). mincore() буквально означает “память в ядре” (memory in core). Его параметрами являются начальный адрес виртуальной памяти, длина адресного пространства и результирующий вектор. mincore() работает с памятью (а не с файлами), поэтому его можно использовать и для проверки, была ли вытеснена анонимная память в своп (swap).
mincore() returns a vector that indicates whether pages of the calling process’s virtual memory are resident in core (RAM), and so will not cause a disk access (pagefault) if referenced. The kernel returns residency information about the pages starting at the address addr, and continuing for length bytes.
Поэтому для повторения фичи vmtouch нам нужно сначала отобразить файл в виртуальную память процесса, даже если мы не собираемся выполнять ни чтение, ни запись.
Теперь у нас есть все необходимое для написания нашего собственного простого vmtouch , чтобы вывести информацию из Page Cache о файле. Я использую Go, потому что, к сожалению, в Python нет простого способа вызвать mincore() syscall:
И сверяем вывод с vmtouch :
Вывод
Как видно из статьи ядро Linux предоставляет широкий набор возможностей для взаимодействия и управления Page Cache, которые на мой взгляд должен знать каждый SRE.
Источник