Подтвердить что ты не робот

Почему операционные системы ограничивают дескрипторы файлов?

Я задаю этот вопрос, пытаясь наилучшим образом исследовать лучший способ реализации сервера очереди сообщений. Почему операционные системы ограничивают количество дескрипторов открытых файлов процессом и глобальной системой? Моя текущая реализация сервера использует zeromq и открывает сокет-подписчик для каждого подключенного клиента websocket. Очевидно, что единственный процесс будет способен обрабатывать клиентов до предела fds. Когда я исследую эту тему, я нахожу много информации о том, как повысить системные ограничения до уровней до 64 тыс. Фдд, но никогда не упоминает, как это влияет на производительность системы и почему она начинается с 1к и ниже? Мой текущий подход заключается в попытке отправки сообщений всем клиентам, использующим сопрограмму в своем собственном цикле, и карту всех клиентов и их каналов подписки. Но я бы просто хотел услышать твердый ответ о ограничениях файлового дескриптора и о том, как они влияют на приложения, которые пытаются использовать их на одном уровне клиента с постоянными подключениями?

4b9b3361

Ответ 1

Возможно, это связано с тем, что значение дескриптора файла является индексом в таблице дескриптора файла. Поэтому число возможных файловых дескрипторов определит размер таблицы. Средние пользователи не хотели бы, чтобы половина их бара использовалась таблицей дескриптора файла, которая может обрабатывать миллионы файловых дескрипторов, которые им никогда не понадобятся.

Ответ 2

Для целей производительности таблица открытых файлов должна быть статически распределена, поэтому ее размер должен быть исправлен. Дескрипторы файлов просто смещаются в эту таблицу, поэтому все записи должны быть смежными. Вы можете изменить размер таблицы, но это требует остановки всех потоков в процессе и выделения нового блока памяти для таблицы файлов, а затем копирования всех записей из старой таблицы в новую. Это не то, что вы хотите делать динамически, особенно когда причина, по которой вы это делаете, состоит в том, что старая таблица заполнена!

Ответ 3

Существуют определенные операции, которые замедляются, когда у вас много потенциальных дескрипторов файлов. Одним из примеров является операция "закрыть все дескрипторы файлов, кроме stdin, stdout и stderr" - единственный способ переносить * - это попытаться закрыть все возможные дескрипторы файлов, кроме тех трех, которые могут стать медленная операция, если вы потенциально можете открыть миллионы файловых дескрипторов.

*: Если вы хотите быть не переносным, вы можете смотреть в /proc/self/fd, но это помимо точки.

Это не особо веская причина, но это причина. Другая причина заключается в том, чтобы просто сохранить багги-программу (то есть, которая "утечка" файловых дескрипторов) из-за слишком большого количества системных ресурсов.

Ответ 4

В системах unix для fork() и fork()/exec() для создания процесса требуется итерация по всем потенциальным дескрипторам файла процесса, пытающимся закрыть их, обычно оставляя лишь несколько дескрипторов файлов, таких как stdin, stdout, stderr нетронутым или перенаправленным в другое место.

Поскольку этот является unix api для запуска процесса, он должен выполняться в любое время, когда создается новый процесс, включая выполнение каждой не встроенной команды, вызываемой в сценариях оболочки.

Другими факторами, которые следует учитывать, является то, что, хотя некоторое программное обеспечение может использовать sysconf(OPEN_MAX) для динамического определения количества файлов, которые могут быть открыты процессом, много программного обеспечения по-прежнему использует библиотеку C по умолчанию FD_SETSIZE, которая обычно составляет 1024 дескриптора, и как таковой никогда не может быть больше, чем многие файлы открываются независимо от любого административно определенного верхнего предела.

Unix имеет устаревший механизм асинхронного ввода-вывода на основе наборов дескрипторов файлов, которые используют смещения бит для представления ожидающих файлов и файлов, которые готовы или находятся в состоянии исключения. Он не очень хорошо масштабируется для тысяч файлов, так как эти наборы дескрипторов необходимо настраивать и очищать каждый раз вокруг runloop. Более новые нестандартные apis появились в основных вариантах unix, включая kqueue() on * BSD и epoll() в Linux для устранения недостатков производительности при работе с большим количеством дескрипторов.

Важно отметить, что select()/poll() по-прежнему используется A LOT программного обеспечения, поскольку в течение длительного времени он был POSIX api для асинхронного ввода-вывода. Современный асинхронный IO-подход POSIX теперь является aio_* API, но, скорее всего, он не конкурирует с API kqueue() или epoll(). Я не использовал aio в гневе, и у него, конечно же, не было бы производительности и семантики, предлагаемых родными подходами, так как они могли бы агрегировать несколько событий для повышения производительности. kqueue() on * BSD имеет действительно хорошую семантику, вызванную фронтом, для уведомления о событиях, позволяя ей заменить select()/poll(), не форсируя большие структурные изменения в вашем приложении. Linux epoll() следует за ведением * BSD kqueue() и улучшает его, что в свою очередь сопровождает руководство Sun/Solaris.

Результатом является то, что увеличение количества разрешенных открытых файлов в системе добавляет как временные, так и пространственные издержки для каждого процесса в системе, даже если они не могут использовать эти дескрипторы на основе используемого api. Существуют также совокупные системные ограничения для количества разрешенных открытых файлов. Это более старое, но интересное резюме настройки для одновременных подключений 100k-200k с использованием nginx на FreeBSD дает некоторое представление о накладных расходах для поддержания открытых соединений и другой, охватывающий более широкий спектр систем, но "только", видя 10K-соединений как Mt Everest.

Вероятно, лучшей ссылкой для системного программирования Unix является W. Richard Stevens Расширенное программирование в среде Unix