Как заставить процесс освободить файловые дескрипторы?

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

Ответ

Освобождение файловых дескрипторов (FD) — частая задача при устранении проблем с "Too many open files". Вот методы, которые применяю в зависимости от ситуации.

1. Диагностика (поиск виновника):

# Нахожу PID процесса с большим количеством открытых FD
lsof | awk '{print $2}' | sort | uniq -c | sort -rn | head

# Или для конкретного процесса
ls -la /proc/<PID>/fd | wc -l

# Смотрю, какие именно файлы открыты процессом
lsof -p <PID>

# Или ищу процесс, блокирующий конкретный файл
lsof /path/to/locked/file

2. Методы освобождения дескрипторов:

А. Корректное завершение процесса (предпочтительно):

# Отправляю SIGTERM для graceful shutdown
kill <PID>

# Если не срабатывает, через 30 секунд отправляю SIGKILL
kill -9 <PID>

Б. Принудительное освобождение файла с помощью fuser:

# Показываю процессы, использующие файл
fuser -v /path/to/file

# Принудительно завершаю все процессы, использующие файл
fuser -k /path/to/file

# Для TCP/UDP портов (например, если процесс не отпускает сокет)
fuser -k 8080/tcp

В. Закрытие конкретного дескриптора в работающем процессе (осторожно!):

# Использую gdb для вызова close() на конкретном FD
# Например, чтобы закрыть FD 42 в процессе 12345
gdb -p 12345 -ex "call close(42)" -ex "detach" -ex "quit"

Важно: Этот метод может привести к нестабильности процесса, если дескриптор активно используется.

Г. Перезапуск сервиса через systemd:

# Для systemd-сервисов
sudo systemctl restart nginx.service

# Или с принудительным завершением
sudo systemctl kill -s KILL nginx.service

3. Практический пример из инцидента: При обработке инцидента с исчерпанием лимита файловых дескрипторов у Nginx в production:

# 1. Нашел PID основного процесса nginx
cat /var/run/nginx.pid

# 2. Посчитал открытые FD (их было > 8000 при лимите 8192)
ls -1 /proc/$(cat /var/run/nginx.pid)/fd | wc -l

# 3. Увидел, что многие FD указывают на лог-файлы
lsof -p $(cat /var/run/nginx.pid) | grep ".log$"

# 4. Принял решение о ротации логов и graceful reload
sudo logrotate -f /etc/logrotate.d/nginx
sudo nginx -s reload  # Отправляет SIGHUP, заставляет nginx переоткрыть лог-файлы

# 5. Проверил результат
ls -1 /proc/$(cat /var/run/nginx.pid)/fd | wc -l  # Стало ~50

4. Профилактика:

# Увеличиваю системные лимиты
echo "fs.file-max = 65536" >> /etc/sysctl.conf
sysctl -p

# Увеличиваю лимиты для конкретного пользователя/процесса
ulimit -n 65536

# В systemd-сервисе добавляю в .service файл
[Service]
LimitNOFILE=65536