What is cache directory in linux

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) позже в этой серии постов.

Читайте также:  Jingle palette mac os

Теперь давайте посмотрим как использование 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 с файлом и соответствующей информацией. Мы можем получить грязные страницы, найдя там:

Читайте также:  Перезапустить проводник windows 10 cmd

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:

Читайте также:  Linux mint cinnamon wallpapers

И сверяем вывод с vmtouch :

Вывод

Как видно из статьи ядро Linux предоставляет широкий набор возможностей для взаимодействия и управления Page Cache, которые на мой взгляд должен знать каждый SRE.

Источник

Cache directory structure

I’m in the process of implementing caching for my project. After looking at cache directory structures, I’ve seen many examples like:

You get the idea. Another example for storing files, let’s say our file is named IMG_PARTY.JPG , a common way is to put it in a directory named:

Some thoughts come to mind, but I’d like to know the real reasons for this.

Filesystems doing linear lookups find files faster when there’s fewer of them in a directory. Such structure spreads files thin.

To not mess up *nix utilities like rm , which take a finite number of arguments and deleting large number of files at once tends to be hacky (having to pass it though find etc.)

What’s the real reason? What is a «good» cache directory structure and why?

3 Answers 3

Every time I’ve done it, it has been to avoid slow linear searches in filesystems. Luckily, at least on Linux, this is becoming a thing of the past.

However, even today, with b-tree based directories, a very large directory will be hard to deal with, since it will take forever and a day just to get a listing of all the files, never mind finding the right file.

Just use dates. Since you will remove by date. 🙂

If you do ls -l , all the files need to be stat() ed to get details, which adds considerably to the listing time — this happens whether the FS uses hashed or linear structures.

So even if the FS has a capability of coping with incredibly large directory sizes, there are good reasons not to have large flat structures (They’re also a pig to back up)

I’ve benchmarked GFS2 (clustered) with 32,000 files in a directory or arranged in a tree structure — recursive listings were around 300 times faster than getting a listing when they were all in a flat structure (could take up to 10 minutes to get a directory listing)

EXT4 showed similar ratios but as the end point was only a couple of seconds most people wouldn’t notice.

Источник

clion change default cache directory

Currently, for linux operating systems, the cache and index folders for clion are defaulted to /home/.Clion12/. I am on an NFS and want the cache directory and indexing directories to point to my internal ssd. Where can I change this?

3 Answers 3

I just found the answer to this. In the bin directory where clion.sh is kept, there is an ideas.properties file. You can change the default location there.

As @StevenCombs said, the file idea.properties is in the same bin directory where clion.sh is found, and the variable is idea.system.path .

I set it up like this. First, on the command line I did:

And then in the $/bin/idea.properties file, I added

CLion appears to populate the CLion/system directories under my personal cache directory.

The problem was solved for me after I have created an idea.properties file in

/.Clion2019.2/config then populated it with:

After this, the cache is build in /someFolder, not flooding my home folder anymore.

Not the answer you’re looking for? Browse other questions tagged clion jetbrains-ide or ask your own question.

Hot Network Questions

Subscribe to RSS

To subscribe to this RSS feed, copy and paste this URL into your RSS reader.

site design / logo © 2021 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2021.10.8.40416

By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.

Источник

Оцените статью