Зачем
Disk slow, RAM fast. Page cache держит блоки файлов в памяти, чтобы:
- Повторное чтение того же файла не лезло на диск
- Запись возвращалась мгновенно (writeback позже)
- mmap-файлы работали без явного
read()
Это прозрачно: программы пишут в обычные файлы, ядро само
кеширует. Управление - большая часть linux-vm подсистемы.
free -h: что значат колонки
total used free shared buff/cache available
Mem: 16Gi 4.0Gi 500Mi 100Mi 12Gi 11Gi
Swap: 4Gi 0 4Gi
- used - реально занято процессами + ядром
- free - никем не используется (обычно мало!)
- buff/cache - page cache; доступно процессам по требованию
- available - оценка сколько новый процесс реально получит (примерно used + reclaimable cache)
«Linux ate my RAM» - миф. Если available большой - всё ок, кеш
отдадут под allocation мгновенно. Главное смотреть на available, не на free.
Buffers vs Cache
Исторически:
- Buffers - кеш block-level (raw блоки устройства)
- Cache - кеш ФС-уровня (файлы)
На современных kernel'ах разница почти стёрлась, оба попадают в
page cache. В free они сложены в buff/cache.
Dirty pages и writeback
Когда программа пишет в файл, страница помечается dirty (грязная). Ядро сбрасывает её на диск асинхронно - через writeback.
cat /proc/meminfo | grep -E 'Dirty|Writeback'
# Dirty: 12 MB ← модифицированы, ещё не на диске
# Writeback: 0 MB ← в процессе записи прямо сейчас
Контролируется sysctl'ями:
cat /proc/sys/vm/dirty_ratio # % RAM с которого ЗАСТАВЛЯЕТ записать (default 20)
cat /proc/sys/vm/dirty_background_ratio # % с которого начинает в фоне (default 10)
cat /proc/sys/vm/dirty_expire_centisecs # как долго dirty может ждать (default 30s)
Слишком большой dirty_ratio = долгие fsync-stall'ы при сбросе. Слишком маленький = больше I/O. На прод-БД часто тюнят.
sync, fsync, sync()
sync # сбросить ВСЕ dirty pages всех ФС (можно перед reboot)
В программах:
fsync(fd)- гарантирует что данные файла на дискеfdatasync(fd)- то же, но без метаданных (быстрее)O_SYNCфлаг приopen()- каждая запись синхронная (медленно)
БД и журнальные системы используют fsync на каждый commit - отсюда важность fast NVMe vs HDD для transaction throughput.
drop_caches - освободить кеш руками
sync # сначала сбросить dirty
echo 3 | sudo tee /proc/sys/vm/drop_caches # 1=pagecache 2=dentries+inodes 3=всё
Зачем: бенчмаркить cold-cache производительность, провоцировать reload, debugging. На проде НЕ делать - потом первое чтение большого файла станет тормозным.
Direct I/O - обход кеша
Когда программа сама управляет кешированием (БД, видеоплееры):
open(path, O_DIRECT | O_RDWR, ...);
Чтение/запись летят прямо на диск, минуя page cache. Минус - все размеры и offset'ы должны быть выровнены по сектору.
Readahead
Ядро видит последовательное чтение файла и превентивно загружает
следующие блоки - readahead:
blockdev --getra /dev/sda # размер readahead в 512-байтных секторах
blockdev --setra 16384 /dev/sda # увеличить (для streaming workload)
Большой readahead помогает sequential, мешает random (читает то что не пригодится).
fadvise / madvise
Программа может подсказать ядру:
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED)- «забудь этот файл из кеша» (после копирования больших файлов хорошо звать, чтобы не вытесняли горячие данные)POSIX_FADV_SEQUENTIAL- увеличить readaheadPOSIX_FADV_RANDOM- отключить readahead
Утилита vmtouch - портит/прогревает кеш руками для бенчмарков.
Page cache в cgroups v2
В контейнере page cache считается per-cgroup. Лимит memory.max
включает и кеш. Поэтому контейнер с маленьким лимитом памяти,
читающий большой файл, может OOM'нуться даже без heap-allocations.