Сокет принимает - «Слишком много открытых файлов» - PullRequest
52 голосов
/ 19 мая 2009

Я работаю над школьным проектом, в котором мне нужно было написать многопоточный сервер, и сейчас я сравниваю его с apache, выполняя некоторые тесты для него. Я использую autobench, чтобы помочь с этим, но после того, как я запустил несколько тестов или, если я установил слишком высокую скорость (около 600+) для установления соединений, я получаю ошибку «Too many open files»

После того, как я закончу с запросом, я всегда делаю close() в сокете. Я также пытался использовать функцию shutdown(), но ничего не помогло. Есть ли способ обойти это?

Ответы [ 10 ]

53 голосов
/ 19 мая 2009

Есть несколько мест, где Linux может иметь ограничения на количество дескрипторов файлов, которые вам разрешено открывать.

Вы можете проверить следующее:

cat /proc/sys/fs/file-max

Это даст вам системные ограничения файловых дескрипторов.

На уровне оболочки, это скажет вам ваш личный лимит:

ulimit -n

Это можно изменить в /etc/security/limits.conf - это параметр nofile.

Однако, если вы правильно закрываете сокеты, вы не должны получать это, если не открываете много одновременных соединений. Похоже, что-то мешает вашим сокетам закрыться надлежащим образом. Я бы проверил, что с ними обращаются правильно.

24 голосов
/ 21 декабря 2011

У меня была похожая проблема. Быстрое решение:

ulimit -n 4096

объяснение заключается в следующем - каждое соединение с сервером является файловым дескриптором. В CentOS, Redhat и Fedora, возможно, других, ограничение на количество пользователей файлов составляет 1024 - понятия не имею, почему. Это легко увидеть при вводе: ulimit -n

Обратите внимание, что это не имеет никакого отношения к системным файлам max

В моем случае это была проблема с Redis, поэтому я сделал:

ulimit -n 4096
redis-server -c xxxx

в вашем случае вместо redis вам нужно запустить сервер.

14 голосов
/ 03 июня 2010

TCP имеет функцию под названием «TIME_WAIT», которая обеспечивает чистое закрытие соединений. Для того, чтобы прослушивание оставалось некоторое время после закрытия сокета, требуется один конец соединения.

На высокопроизводительном сервере важно, чтобы клиенты TIME_WAIT обращались к клиентам, а не к серверу. Клиенты могут позволить себе открыть порт, тогда как занятый сервер может быстро исчерпать порты или иметь слишком много открытых FD.

Чтобы добиться этого, сервер никогда не должен сначала закрывать соединение - он всегда должен ждать, пока клиент закроет его.

9 голосов
/ 01 июня 2011

Используйте lsof -u `whoami` | wc -l, чтобы узнать, сколько открытых файлов у пользователя

7 голосов
/ 21 сентября 2015

Это означает, что максимальное количество одновременно открытых файлов.

Решено:

В конце файла /etc/security/limits.conf необходимо добавить следующие строки:

* soft nofile 16384
* hard nofile 16384

В текущей консоли от root (sudo не работает) сделать:

ulimit -n 16384

Хотя это необязательно, если есть возможность перезапустить сервер.

В /etc/nginx/nginx.conf файле для регистрации нового значения worker_connections, равного 16384 делению на значение worker_processes.

Если не сделал ulimit -n 16384, нужно перезагрузить компьютер, тогда проблема исчезнет.

PS:

Если после ремонта видно в логах error accept() failed (24: Too many open files):

В конфигурации nginx, Пропевия (например):

worker_processes 2;

worker_rlimit_nofile 16384;

events {
  worker_connections 8192;
}
6 голосов
/ 21 мая 2013

У меня тоже была эта проблема. У вас есть утечка дескриптора файла. Вы можете отладить это, распечатав список всех дескрипторов открытых файлов (в системах POSIX):

void showFDInfo()
{
   s32 numHandles = getdtablesize();

   for ( s32 i = 0; i < numHandles; i++ )
   {
      s32 fd_flags = fcntl( i, F_GETFD ); 
      if ( fd_flags == -1 ) continue;


      showFDInfo( i );
   }
}

void showFDInfo( s32 fd )
{
   char buf[256];

   s32 fd_flags = fcntl( fd, F_GETFD ); 
   if ( fd_flags == -1 ) return;

   s32 fl_flags = fcntl( fd, F_GETFL ); 
   if ( fl_flags == -1 ) return;

   char path[256];
   sprintf( path, "/proc/self/fd/%d", fd );

   memset( &buf[0], 0, 256 );
   ssize_t s = readlink( path, &buf[0], 256 );
   if ( s == -1 )
   {
        cerr << " (" << path << "): " << "not available";
        return;
   }
   cerr << fd << " (" << buf << "): ";

   if ( fd_flags & FD_CLOEXEC )  cerr << "cloexec ";

   // file status
   if ( fl_flags & O_APPEND   )  cerr << "append ";
   if ( fl_flags & O_NONBLOCK )  cerr << "nonblock ";

   // acc mode
   if ( fl_flags & O_RDONLY   )  cerr << "read-only ";
   if ( fl_flags & O_RDWR     )  cerr << "read-write ";
   if ( fl_flags & O_WRONLY   )  cerr << "write-only ";

   if ( fl_flags & O_DSYNC    )  cerr << "dsync ";
   if ( fl_flags & O_RSYNC    )  cerr << "rsync ";
   if ( fl_flags & O_SYNC     )  cerr << "sync ";

   struct flock fl;
   fl.l_type = F_WRLCK;
   fl.l_whence = 0;
   fl.l_start = 0;
   fl.l_len = 0;
   fcntl( fd, F_GETLK, &fl );
   if ( fl.l_type != F_UNLCK )
   {
      if ( fl.l_type == F_WRLCK )
         cerr << "write-locked";
      else
         cerr << "read-locked";
      cerr << "(pid:" << fl.l_pid << ") ";
   }
}

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

Если ваш сервер порождает подпроцессы. Например. если это сервер в стиле 'fork', или если вы запускаете другие процессы (например, через cgi), вы должны обязательно создать свои файловые дескрипторы с помощью «cloexec» - как для реальных файлов, так и для сокетов.

Без cloexec, каждый раз, когда вы запускаете или порождаете, все дескрипторы открытых файлов клонируются в дочернем процессе.

Также очень просто не закрывать сетевые сокеты - например, просто оставив их, когда удаленная сторона отключится. Это будет течь ручками, как сумасшедшие.

4 голосов
/ 19 мая 2009

может пройти некоторое время, прежде чем закрытый сокет действительно освободится

lsof для просмотра списка открытых файлов

cat /proc/sys/fs/file-max чтобы узнать, существует ли системный лимит

2 голосов
/ 10 июля 2017

Просто еще одна информация о CentOS. В этом случае при использовании systemctl запускается процесс. Вы должны изменить системный файл ==> /usr/lib/systemd/system/processName.service . Имеет эту строку в файле:

LimitNOFILE=50000

И просто перезагрузите вашу систему conf:

systemctl daemon-reload
1 голос
/ 21 мая 2013

У меня была та же проблема, и я не удосужился проверить возвращаемые значения вызовов close (). Когда я начал проверять возвращаемое значение, проблема таинственным образом исчезла.

Я могу только предположить, что сбой оптимизации компилятора (в моем случае gcc) предполагает, что вызовы close () не имеют побочных эффектов и могут быть опущены, если их возвращаемые значения не используются.

1 голос
/ 19 мая 2009

Когда ваша программа имеет больше открытых дескрипторов, чем ulimit открытых файлов (ulimit -a перечислит это), ядро ​​откажется открывать больше файловых дескрипторов. Убедитесь, что у вас нет утечек файловых дескрипторов - например, запустив его некоторое время, затем остановив и посмотрев, открыты ли какие-либо дополнительные fds, когда он находится в режиме ожидания, - и, если это все еще проблема, измените ulimit nofile для вашего пользователь в /etc/security/limits.conf

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...