Ответы:
Я думаю, что это отвечает на ваш вопрос:
От Ричарда Стивенса (rstevens@noao.edu):
Основное отличие заключается в том, что fd_set в select () является битовой маской и поэтому имеет некоторый фиксированный размер. Было бы возможно, чтобы ядро не ограничивало этот размер при компиляции ядра, позволяя приложению определять FD_SETSIZE для чего угодно (как подразумевается в комментариях в системном заголовке сегодня), но это требует больше работы. Ядро 4.4BSD и библиотечная функция Solaris имеют этот предел. Но я вижу, что BSD / OS 2.1 теперь была закодирована, чтобы избежать этого ограничения, так что это выполнимо, только небольшой вопрос программирования. :-) Кто-то должен подать отчет об ошибке Solaris и посмотреть, будет ли он когда-нибудь исправлен.
Однако с помощью poll () пользователь должен выделить массив структур pollfd и передать количество записей в этом массиве, поэтому нет никаких фундаментальных ограничений. Как отмечает Каспер, в системах poll () меньше, чем select, поэтому последняя более переносима. Кроме того, в оригинальных реализациях (SVR3) вы не могли установить дескриптор на -1, чтобы сказать ядру игнорировать запись в структуре pollfd, что затрудняло удаление записей из массива; SVR4 обходит это. Лично я всегда использую select () и редко poll (), потому что я портирую свой код и в среды BSD. Кто-то может написать реализацию poll (), которая использует select () для этих сред, но я никогда не видел ее. И select (), и poll () стандартизированы POSIX 1003.1g.
Письмо, указанное выше, по крайней мере, так же стара, как и 2001; poll()
команда сейчас (2017) поддерживается во всех современных операционных систем , в том числе - BSD. На самом деле, некоторые люди считают, что это select()
следует считать устаревшим . Помимо мнений, проблемы переносимости poll()
больше не являются проблемой современных систем. Кроме того, с epoll()
тех пор был разработан (вы можете прочитать страницу руководства ), и продолжает расти популярность.
Для современного развития вы, вероятно, не хотите использовать select()
, хотя в этом нет ничего явно неправильного. poll()
и это более современная эволюция epoll()
, обеспечивающая те же функции (и больше), что и select()
без страданий от ограничений в ней.
select
или poll
:(
При select()
вызове вы создаете три битовые маски, чтобы пометить, какие сокеты и дескрипторы файлов вы хотите отслеживать на предмет чтения, записи и ошибок, а затем операционная система отмечает, какие из них действительно выполняли какие-то действия; poll()
вы создали список идентификаторов дескрипторов, и операционная система помечает каждый из них с видом события , которые произошли.
select()
Метод довольно неуклюжий и неэффективный.
Обычно процессу доступно более тысячи потенциальных файловых дескрипторов. Если в длительном процессе открыто только несколько дескрипторов, но хотя бы одному из них назначено большое число, то передаваемая битовая маска должна select()
быть достаточно большой, чтобы вместить этот самый высокий дескриптор - поэтому целые диапазоны в сотни бит будут быть неустановленным, что операционная система должна переключаться между циклами при каждом select()
вызове, чтобы обнаружить, что они не установлены.
После select()
возврата вызывающая сторона должна пройти через все три битовые маски, чтобы определить, какие события произошли. В очень многих типичных приложениях только один или два файловых дескриптора получат новый трафик в любой момент, но все три битовых маски должны быть прочитаны до конца, чтобы определить, какие это дескрипторы.
Поскольку операционная система сообщает вам об активности, переписывая битовые маски, они разрушаются и больше не помечаются списком файловых дескрипторов, которые вы хотите прослушать. Либо вам нужно перестроить всю битовую маску из какого-либо другого списка, который вы храните в памяти, либо сохранять дубликаты каждой битовой маски и memcpy()
блока данных поверх разрушенных битовых масок после каждого select()
вызова.
Таким образом, этот poll()
подход работает намного лучше, потому что вы можете продолжать использовать одну и ту же структуру данных.
Фактически, poll()
он вдохновил еще один механизм в современных ядрах Linux: он epoll()
еще больше улучшает механизм, позволяющий сделать еще один скачок в масштабируемости, поскольку современные серверы часто хотят обрабатывать десятки тысяч соединений одновременно. Это хорошее введение в усилия:
http://scotdoyle.com/python-epoll-howto.html
В то время как эта ссылка имеет несколько хороших графиков, показывающих преимущества epoll()
(вы заметите, что select()
на данный момент считается настолько неэффективным и старомодным, что на этих графиках даже нет линии!):
http://lse.sourceforge.net/epoll/index.html
Обновление: вот еще один вопрос переполнения стека, ответ на который дает еще более подробную информацию о различиях:
Предостережения о выборе / опросе против реактора epoll в Twisted
Оба они медленные и в основном одинаковые , но разные по размеру и некоторым функциям!
Когда вы пишете итератор, вам нужно копировать множество select
каждый раз! Пока poll
исправлена такая проблема, чтобы иметь красивый код. Другое отличие заключается в том, что poll
по умолчанию может обрабатывать более 1024 файловых дескрипторов (FD). poll
может обрабатывать различные события, чтобы сделать программу более читабельной, вместо того, чтобы иметь много переменных для обработки такого рода работы. Операции в poll
и select
линейные и медленные из-за большого количества проверок.