В вызове 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