Зачем вообще memfd_create и почему от него «пропадает» память
memfd_create — системный вызов Linux, который создаёт анонимный файловый объект и возвращает файловый дескриптор. Для приложения это «файл»: его можно читать/писать, делать mmap, передавать через Unix-сокеты другим процессам. Но хранится он в памяти (учёт обычно идёт через shmem/tmpfs и связанные маппинги), а не как обычный файл на диске.
Практически memfd встречается в браузерах, песочницах, JIT-компиляторах, контейнерных рантаймах, агентах обновления и сервисах, которые не хотят оставлять следы во временных каталогах. Для администратора это иногда выглядит как «пропала RAM»: на диске пусто, du ничего не показывает, а MemAvailable падает.
Параллельно есть классическая история с deleted files: файл удалён из дерева каталогов, но процесс держит открытый дескриптор. Тогда df видит занятое место (иноды/блоки всё ещё аллоцированы), а du — нет, потому что имени уже нет. По ощущениям это очень похоже на memfd: «объект есть, а пути нет», только в случае deleted files чаще страдает место на диске, а в случае memfd — RAM.
du vs df: почему цифры расходятся (и это нормально)
du суммирует размеры файлов, которые достижимы по путям в каталоге. df показывает занятое место на уровне файловой системы: всё, что реально заняло блоки/иноды, включая уже удалённые из каталога, но ещё открытые файлы.
Типовые причины расхождения:
- deleted files: лог удалили/ротировали, но процесс продолжает писать в старый inode.
- Права доступа:
duне может зайти в каталог и пропускает часть дерева. - Снапшоты/CoW (btrfs/zfs/lvm):
dfучитывает физические блоки,duвидимую версию. - Разные точки монтирования:
duуходит в примонтированное, аdfсмотрит конкретную ФС.
Важно не смешивать плоскости: memfd почти всегда про память (shmem/tmpfs/анонимные маппинги), а расхождение du/df — чаще про дисковую ФС и удалённые, но открытые файлы.
Перед тем как углубляться в диагностику, зафиксируйте базовые метрики: свободное место по df, реальную видимую файловую «массу» по du и динамику памяти по free/vmstat. Это поможет отделить «утекло место на диске» от «давит память».

deleted files: как быстро найти виновника через lsof +L1
Самый быстрый способ найти процессы, удерживающие удалённые файлы, — lsof +L1. Опция +L1 выводит файлы с количеством ссылок меньше 1: они удалены из каталога, но ещё открыты.
lsof +L1
Если вывод огромный, начните с среза:
lsof +L1 | head -n 50
Смотрите на SIZE/OFF и на путь с пометкой (deleted) — именно эти inode держат место, которое видит df, но не видит du.
Практический подход: если это логи, правильнее заставить процесс переоткрыть файлы (релоад/сигнал, если поддерживается) или сделать контролируемый перезапуск. «Удалить ещё раз» не поможет: имени уже нет, и inode освободится только при закрытии последнего дескриптора.
/proc/<pid>/fd: что на самом деле открыто у процесса
Когда нашли PID, идите в /proc: каталог /proc/<pid>/fd содержит симлинки на все открытые дескрипторы (файлы, сокеты, anon_inode и т.д.). Это полезно даже в минимальных окружениях, где нет lsof.
ls -l /proc/1234/fd
Признаки:
- удалённый файл будет с
(deleted)в конце; - memfd обычно выглядит как
/memfd:NAME(имя задаёт приложение), то есть путь «псевдофайловый», а объект живёт в памяти.
Быстрые фильтры по одному процессу:
ls -l /proc/1234/fd | grep '(deleted)'
ls -l /proc/1234/fd | grep memfd
tmpfs/shmem и «память как файловая система»: где здесь место memfd_create
С точки зрения учёта Linux memfd тесно связан с shmem/tmpfs: объект существует как «файл», но его страницы — это память. Поэтому при расследовании «куда делась RAM» полезно посмотреть на tmpfs, но помнить, что цифры не всегда напрямую отвечают на вопрос «кто виноват»: страницы могут быть shared, могут переходить в private из-за COW, а часть может по-разному вести себя под давлением памяти.
Быстрые команды для контекста:
mount | grep tmpfs
df -hT | grep tmpfs
Если у вас наблюдается регулярное давление на память, имеет смысл параллельно привести в порядок лимиты/воркеры сервисов. Для веб-стека это часто начинается с тюнинга пулов и лимитов: пригодится разбор настройки PHP-FPM под нагрузку на VDS.
Почему RSS не отвечает на вопрос «кто съел RAM»
Частая ловушка — смотреть только RSS в ps/top. RSS показывает резидентный набор страниц процесса, но:
- не объясняет, какие страницы shared (и кто «платит» за них);
- не даёт детализации по типам маппингов (heap/stack/file-backed/shmem/anonymous);
- может казаться «нормальным», пока значимая часть памяти распределена через shared mappings.
Для более честной оценки используйте PSS (proportional set size): shared-страницы делятся пропорционально числу процессов. Главный источник — /proc/<pid>/smaps и суммарный /proc/<pid>/smaps_rollup.

smaps и smaps_rollup: как увидеть anonymous mappings, shmem и вклад процесса
/proc/<pid>/smaps детализирует каждый memory mapping: диапазон адресов, права, backing (если есть) и метрики Rss, Pss, Private_Clean, Private_Dirty, Shared_Clean, Shared_Dirty и другие. Здесь обычно видно, где у процесса heap/stack/anon, где file-backed, где shmem/tmpfs/memfd.
Для быстрого итога по процессу используйте smaps_rollup:
cat /proc/1234/smaps_rollup
1) Снять PSS/RSS по группе процессов
Если сервис порождает воркеры, смотрите PSS суммарно (а не только RSS каждого):
for p in $(pgrep -f 'your-service-name'); do echo "== PID $p =="; grep -E '^(Rss|Pss):' /proc/$p/smaps_rollup; done
2) Найти маппинги memfd/shmem внутри smaps
В smaps заголовок каждого маппинга содержит «имя» (путь или метку). Часто достаточно точечного поиска:
grep -n 'memfd' /proc/1234/smaps | head -n 50
grep -nE 'shmem|/dev/shm|tmpfs' /proc/1234/smaps | head -n 50
Дальше оценивайте соседние строки с Pss и особенно Private_Dirty: это то, что сложнее «отжать» без влияния на процесс и что чаще всего становится причиной реального давления памяти.
Связка fd и smaps: как подтвердить, что «файл в памяти» реально съедает RAM
Сам факт наличия /memfd:... в /proc/<pid>/fd не доказывает, что именно он съедает память: дескриптор может быть открыт, но страницы не обязаны быть реально резидентными. Подтверждение делается через smaps: находите соответствующие маппинги и смотрите их Pss/Rss/Private_Dirty.
Рабочий порядок расследования:
- Фиксируете симптомы: падение
MemAvailable, рост swap, OOM. - Находите PID(ы), которые коррелируют по памяти (любым привычным способом).
- Проверяете
ls -l /proc/<pid>/fdнаmemfdи(deleted). - Смотрите
/proc/<pid>/smaps_rollup, затем точечно/proc/<pid>/smapsпо найденным меткам.
Идея простая: виноват не «memfd вообще», а конкретные страницы. Поэтому всегда подтверждайте гипотезу цифрами
PssиPrivate_Dirtyизsmaps.
Что делать после диагностики (и чего не делать)
Дальше решения зависят от типа проблемы:
- deleted files: переоткрыть файлы (релоад/сигнал) или сделать контролируемый перезапуск. Место освободится только после закрытия последнего fd.
- memfd/shmem/anonymous mappings: это поведение приложения. Ищите настройки лимитов кэша/воркеров, утечки, регрессии в версии. Иногда помогает политика регулярных рестартов воркеров с лимитами.
Анти-практики: «убить -9 наугад» в проде без понимания роли процесса; попытки что-то «удалять» в /proc (это не обычная ФС); и игнорирование PSS в пользу RSS при большом количестве shared mappings. Если сервис должен самовосстанавливаться при утечках/зависаниях, полезно иметь мониторинг и перезапуск по правилам: см. настройку Monit для рестартов и алертов на VDS.
Мини-шпаргалка команд
# Найти удалённые, но открытые файлы (du не видит, df видит)
lsof +L1
# Посмотреть открытые fd процесса
ls -l /proc/1234/fd
# Deleted files у конкретного PID
ls -l /proc/1234/fd | grep '(deleted)'
# memfd у конкретного PID
ls -l /proc/1234/fd | grep memfd
# Быстрый итог по памяти процесса (PSS/RSS)
cat /proc/1234/smaps_rollup
# Точечный поиск memfd-маппингов
grep -n 'memfd' /proc/1234/smaps | head -n 50
Итог
Если «не сходятся» du и df — почти всегда начинайте с deleted files: lsof +L1 и проверка /proc/<pid>/fd быстро покажут виновника. Если же «исчезает» RAM или непонятно, кто создаёт давление на память, переходите на уровень маппингов: /proc/<pid>/smaps_rollup и smaps объяснят вклад через PSS и покажут, где именно живут anonymous mappings, shmem/tmpfs и участки, связанные с memfd_create. Такой подход превращает «магические» симптомы в конкретные PID и конкретные сегменты памяти, с которыми уже можно работать настройками сервиса или контролируемыми рестартами.


