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

Ответ

Освобождение файловых дескрипторов (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

Ответ 18+ 🔞

Ах ты ж ёпта, ну вот опять эта хуйня с файловыми дескрипторами! «Too many open files», блядь, классика жанра. Сидишь такой, а тут тебе — овердохуища файлов открыто, и всё, приехали. Ладно, слушай сюда, как я с этим обычно воюю.

1. Сначала ищем, кто виноват (диагностика, ёб твою мать):

# Ищу, у какого процесса больше всего открытых 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)
kill <PID>

# Если через 30 секунд он ещё живёт — тут уже извини, дружок, получай SIGKILL
kill -9 <PID>

Б. Принудительно освободить файл через fuser (когда нужно быстро):

# Смотрю, какие процессы прилипли к файлу
fuser -v /path/to/file

# А потом просто выношу всех, кто его использует
fuser -k /path/to/file

# То же самое для портов, если сокет не отпускает
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 в проде обосрался: Было дело, лимит FD подошёл к концу. Делал вот так:

# 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. Проверил — стало около 50 FD, а не 8000. Удивление пиздец!
ls -1 /proc/$(cat /var/run/nginx.pid)/fd | wc -l

4. Профилактика, чтобы потом не бздеть:

# Поднимаю системный лимит
echo "fs.file-max = 65536" >> /etc/sysctl.conf
sysctl -p

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

# Для systemd-сервиса прописываю в .service файл
[Service]
LimitNOFILE=65536

Вот так вот, чувак. Главное — не паниковать, когда видишь эту ошибку. Почти всегда находится какая-нибудь хитрая жопа в виде незакрытых логов или сокетов. Находишь — и ебёшь.