Сервер внезапно перестал отвечать, а в логах ошибка Too many open files? Дело не в нехватке памяти или перегруженном процессоре — просто ваш процесс исчерпал свои файловые дескрипторы. Под катом расскажу, что это такое, почему они заканчиваются, как быстро обнаружить проблему и как её пофиксить.

Что такое файловые дескрипторы

Файловый дескриптор — это простое число, через которое процесс взаимодействует с файлом, сокетом или любым другим I/O-ресурсом. В Unix-подобных системах каждый новый вызов open() или socket() возвращает свободный дескриптор, но 0, 1 и 2 обычно заняты под stdin, stdout и stderr, а дальше в ход идут остальные. В Windows логика аналогичная, только там у процесса есть таблица хэндлов, которая позволяет работать с файлами и объектами ядра.

По умолчанию Linux выдаёт процессу 1024 или 4096 дескрипторов. Как только процесс полностью «съедает» эту квоту, все попытки открытия файлов или принятия соединения будут неудачны.

Ядро Windows технически может отсыпать процессу вплоть до 16 777 216 хэндлов, но в реальности столько не используется. Если у вас открыто около 10 тыс. объектов — это уже аномалия и пора искать утечку ресурсов.

Отдельно про Linux…

Для начинающих, в Linux есть два важных лимита: 

  • Глобальный лимит (fs.file-max в /proc/sys/fs/file-max) задаёт максимальное число файловых ресурсов, которые ядро может одновременно открыть. Эти дескрипторы распределяются процессами и если когда-либо будет достигнут потолок, приложение упадёт с ошибкой ENFILE, а в системных логах (в том же dmesg) появится VFS: file-max limit reached. Но такое бывает редко и только при экстремальной нагрузке на файловую систему.

  • Лимит именно на процесс (soft/hard лимиты ulimit) — каждый процесс при запуске получает свой порог дескрипторов. Если процесс пытается открыть файл после того, как достигнут soft-лимит, вызовы типа open() или socket() будут падать с ошибкой EMFILE — той самой too many open files.

Есть отдельный нюанс с поллингом дескрипторов — это системное I/O-мультиплексирование. Иногда процесс использует select() для отслеживания дескрипторов, тогда он ограничен параметром FD_SETSIZE. Это ограничение зашито на уровне компиляции системных библиотек (те самые 1024), и сигнал EMFILE может появиться даже раньше, чем сработают лимиты ОС. Поэтому при высоконагруженных серверах лучше перейти на poll/epoll или повысить FD_SETSIZE, хотя это сделать сложнее. 

К слову, если приложение на Go, Node.js или с nginx/haproxy упирается в лимит, оно почти всегда использует epoll. У него такого жёсткого ограничения нет — он ограничен только лимитом дескрипторов процесса.

Главные симптомы и как их найти

Диагностика в Linux

Самый очевидный симптом в Linux — ошибка Too many open files в логах сервиса. В этом случае диагностику проводим через поиск самого «раздутого» процесса. Для этого первым делом проверьте текущие лимиты (по тому процессу, который подозреваем): 

ulimit -n

cat /proc/$$/limits

Если сервис крутится под systemd, лимит может быть задан в его unit-файле через параметр LimitNOFILE.

Дальше смотрим, сколько файлов реально открыто процессом прямо сейчас. Можно заглянуть в директорию /proc/<PID>/fd/ и посчитать файлы там или сделать это через:

lsof -p <PID> | wc -l

Также проверьте глобальные лимиты системы:

cat /proc/sys/fs/file-max

cat /proc/sys/fs/file-nr

Первое число в выводе покажет текущее количество выделенных дескрипторов, а третье (совпадает с file-max) — системный максимум. Если первое значение близко к пределу, значит, система упёрлась в глобальный лимит. В этом случае изучите dmesg на наличие строчек file-max limit reached.

Наконец, оцените активность, чтобы локализовать саму «утечку»:

lsof -p <PID>

ss -s

Это поможет понять, что именно держит процесс. Зачастую одна из частей сервиса просто не закрывает файлы логов или плодит соединения с БД (тут тоже поможет статистика по сокетам от ss).

Есть ещё одна неочевидная утечка — расплодившиеся inotify-слушатели, которые также потребляют файловые дескрипторы. Если же в коде всё чисто, а они вымываются из-за наплыва трафика (пиковые часы или дудосы), придётся перераспределить нагрузку балансировщиками и настроить кэширование.

Диагностика в Windows

В Windows смотрите на счётчик хэндлов в Process Explorer от Sysinternals или в Performance Monitor. Больше тут сказать нечего. 

Однако отмечу, что Too many open files в Windows может возникнуть не только из-за дескрипторов, но и от библиотек ввода-вывода (историческое наследие от языка C). Стандартная библиотека разрешает одновременно держать открытыми лишь 512 файловых потоков, если лимит превышен — появится ошибка. Этот предел, конечно, можно расширить, но особого смысла в этом нет.

Как пофиксить проблему

Чтобы сервер перестал падать, нужно либо заставить приложение быстрее отдавать дескрипторы обратно ОС, либо «насыпать» ему лимитов с запасом. Обычно комбинируют оба подхода — подробно расскажу про Linux, пару слов о Win дальше. 

В Linux сначала проверяем программу на утечки дескрипторов — любой open, socket или accept должен закрываться через close(). Кроме того, в языках с GC полагаться на сборщик мусора бессмысленно, поэтому ресурсы нужно освобождать через тот же defer file.Close() в Go или with open(...) в Python.

После увеличиваем лимиты RLIMIT_NOFILE в рамках текущей сессии:

ulimit -n 100000

И для постоянного эффекта прописываем лимиты в /etc/security/limits.conf для конкретного пользователя или сервиса:

youruser soft nofile 200000

youruser hard nofile 200000

Эти изменения вступят в силу только после нового логина пользователя или полного перезапуска сессии.

Есть ещё важный момент с systemd — часто Ubuntu, CentOS 7+, Debian и другие современные дистрибутивы полностью игнорируют limits.conf для сервисов. Если демон запускается через systemd, ulimit в консоли ему никак не поможет. Нужно править непосредственно unit-файл сервиса (Ini, TOML):

[Service]

LimitNOFILE=65535

После правки обязательно перечитайте конфиги и рестартните службу:

systemctl daemon-reload

После глобальный лимит fs.file-max сопоставьте с аппетитами сервисов. Увеличить ресурс можно командой:

sysctl -w fs.file-max=1000000

Не забудьте закрепить результат в /etc/sysctl.conf. Также проверьте параметры inotify.

В Windows можно только влиять на настройки самого процесса. Если дело в ограничениях библиотеки С, вызовите _setmaxstdio(...) на уровне кода приложения или увеличьте лимит потоков файлов. Главное — убедитесь, что ваши самописные службы и сервисы корректно освобождают дескрипторы.

И, конечно же, обновляйте ПО. Часто баги с утечками хэндлов просто фиксят в новых версиях. 

Профилактика падений

Наконец, важен постоянный мониторинг. В Windows для мониторинга подойдёт тот же Process Explorer, а в Linux есть как консольные тулзы, так и классическая связка Prometheus + node_exporter (для опытных). 

Командные утилиты для локальной диагностики

Самый очевидный инструмент — это lsof. Он показывает, что открыто процессом, включая сокеты и файлы на диске. Если список внезапно раздулся — начинайте расследование, но помните, что утилита тяжёлая, и сама может положить процесс, если дескрипторы на границе лимитов. 

Прямая проверка через /proc/ работает гораздо быстрее, особенно когда нужно просто посчитать количество открытых хэндлов без запуска тяжеловесного lsof:

Bash

ls -1 /proc/<PID>/fd | wc -l

Можно использовать и sysdig. Он позволяет в реальном времени посмотреть, какие именно процессы открывают файлы и сокеты, и сколько дескрипторов они утилизируют. Плюс умеет строить отчёты и поддерживает фильтры.

Также рекомендую почитать статью «Шпаргалка по поиску узких мест в Linux: топ утилит на все случаи жизни».

Prometheus + node_exporter

Prometheus + node_exporter — классическая связка для мониторинга Linux-узлов, но подходит для тех, кто уже с ней знаком. В ней: 

  • node_exporter стягивает метрики ядра, в том числе статистику по файловым дескрипторам (ищите метрики node_filefd_allocated и node_filefd_maximum),

  • Prometheus периодически забирает эти данные, которые затем можно вывести на дашборды в Grafana. Там же настраиваются правила алертинга.

К слову, иногда node_exporter по умолчанию отдаёт статистику файловых дескрипторов только для самого себя, а не для всех процессов в ОС. В таком случае или запустите его с правами root, или докрутите кастомные экспортеры для сбора полной картины.

На этом всё. Делитесь в комментариях, ловили ли вы Too many open files и какие «костыли» применяли, чтобы быстро вернуть сервер к жизни?

© 2026 ООО «МТ ФИНАНС»