Оба. df спрашивает у файловой системы, сколько блоков занято; du обходит дерево каталогов и складывает то, что видит. Когда эти двое расходятся, место держит что-то, у чего больше нет имени. Вот как это найти.
1. Убедиться, что расхождение есть
$ clai сравни, что df показывает для / с тем, что du там реально находит→ df -h / && du -shx / 2>/dev/nullFilesystem Size Used Avail Use% Mounted on/dev/nvme1n1p2 915G 871G 44G 96% /348G /
Занято 871 ГБ, найдено 348 ГБ. Вот этот разрыв и есть вся проблема. Флаг -x не даёт du уйти в другие примонтированные файловые системы и раздуть собственный ответ.
2. Проверить иноды прежде, чем гоняться за байтами
$ clai проверь, не кончились ли иноды вместо места→ df -i /Filesystem Inodes IUsed IFree IUse% Mounted on/dev/nvme1n1p2 124960768 3634368 121326400 3% /
Если IUse% упёрся в 100%, а Use% — нет, у вас миллионы мелких файлов и ни одного свободного инода. Это другая беда: место на диске есть, а записать новый файл файловой системе некуда.
3. Найти удалённые файлы, которые всё ещё держат открытыми
$ clai покажи файлы, которые удалены, но всё ещё открыты процессом→ sudo lsof +L1COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAMEpython3 23435 app 3u REG 259,2 1000000000 0 41231 /var/log/app/debug.log (deleted)
Признак — NLINK 0: на файл больше не указывает ни одна запись каталога, поэтому du его не видит, но блоки остаются занятыми, пока не закроется последний файловый дескриптор. Обычно это лог, который кто-то удалил через rm, не перезапустив пишущий в него сервис.
4. Вернуть место
$ clai перезапусти сервис, который держит этот удалённый файл→ sudo systemctl restart app.service
Перезапуск закрывает дескриптор, и ядро сразу освобождает блоки. df падает в ту же секунду — без размонтирования и без перезагрузки.
5. Если перезапустить прямо сейчас нельзя
$ clai обнули удалённый файл, который процесс 23435 держит на дескрипторе 3→ sudo truncate -s 0 /proc/23435/fd/3
Запись через /proc/<pid>/fd/<n> опустошает файл, в который процесс продолжает писать, и при этом не убивает его. Дескриптор остаётся, логирование продолжается, место возвращается сразу.
6. Вторая классика: точка монтирования, под которой что-то спрятано
$ clai проверь, не примонтировано ли что-то поверх каталога, в котором остались файлы→ sudo mount --bind / /mnt/root && du -shx /mnt/root/var14G /mnt/root/var
Если в /var/data писали до того, как туда примонтировали диск, эти файлы так и лежат на корневой файловой системе — занимают место, которое сквозь монтирование не видно. Bind-монтирование корня их показывает.
Грабли
- Запускайте
duот root. От обычного пользователя он молча пропускает каталоги, которые не может прочитать, и вы будете искать фантом там, где на самом деле права доступа. lsof +L1нужны привилегии, чтобы видеть дескрипторы чужих процессов. Безsudoвы получите короткий и обманчиво чистый список.- Не удаляйте активный лог ещё раз. Именно это и создало ситуацию. Для живых логов используйте
truncate -s 0или настройте logrotate сcopytruncate.
Похожие вопросы
Почему место не возвращается после rm? В Unix удаление убирает имя, а не данные. Блоки освобождаются, когда закроется последний открытый дескриптор, — а для работающего сервиса это значит «никогда», до перезапуска.
du и df расходятся на пару процентов — это проблема? Нет. Это зарезервированные блоки (по умолчанию 5% для root на ext4) плюс метаданные файловой системы. Разбираться стоит только с большими разрывами.
Можно найти это без lsof? Да: sudo ls -l /proc/*/fd 2>/dev/null | grep deleted показывает то же самое через таблицу процессов.
Читайте также
- «Кто жрёт CPU прямо сейчас?» — мониторинг системы по-человечески
- Подчистить старые логи и бэкапы одной фразой
- Удалить файлы старше 30 дней в Linux, безопасно
Не вспоминайте флаги lsof в три часа ночи — опишите проблему, а команду напишет CliAI. Установка одной строкой.