Почему после удаления файла в Linux команда `df -h` может не показывать освободившееся место?

«Почему после удаления файла в Linux команда `df -h` может не показывать освободившееся место?» — вопрос из категории Linux, который задают на 23% собеседований Devops Инженер. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Команда df (disk free) показывает статистику на уровне файловой системы, а не на уровне открытых файловых дескрипторов. Место не освобождается для файловой системы до тех пор, пока на файл не перестанут ссылаться все дескрипторы.

Техническая причина: В Unix/Linux файл существует, пока на него есть хотя бы одна жесткая ссылка (hard link). Команда rm удаляет лишь одну ссылку (имя файла в директории). Если какой-либо процесс в момент удаления держал файл открытым, ядро сохраняет связь через дескриптор, и данные на диске продолжают занимать место. Файл становится "удаленным" (не виден в ls), но место будет занято до закрытия дескриптора.

Типичные сценарии в DevOps:

  • Лог-файлы приложения: Процесс пишет в app.log. Администратор удаляет файл (rm app.log), чтобы освободить место, но процесс продолжает писать через открытый дескриптор. Место не освобождается, а новый файл app.log не создается. Решение: использовать logrotate с сигналом на переоткрытие файлов (например, copytruncate или postrotate с kill -HUP).
  • Временные файлы: Скрипт создает и открывает временный файл, но не закрывает его перед удалением.

Диагностика и решение:

# 1. Найти процессы, держащие удаленные файлы открытыми
$ lsof +L1 | grep -i deleted
COMMAND   PID   USER   FD   TYPE DEVICE SIZE/OFF NLINK   NODE NAME
java     1234   app    5w   REG   8,16  2.1G     0      12345 /var/log/app.log (deleted)

# 2. Освободить место можно одним из способов:
#    - Перезапустить процесс (PID 1234): `systemctl restart app.service`
#    - Вручную очистить файл через дескриптор (если это безопасно):
$ : > /proc/1234/fd/5

# 3. Правильное управление логами через logrotate
# /etc/logrotate.d/app
/var/log/app.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 644 app app
    postrotate
        # Сигнал процессу переоткрыть лог-файл
        kill -HUP `cat /var/run/app.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

Вывод df обновится только после того, как все дескрипторы на удаленный файл будут закрыты.