Я видел много сравнений, которые говорят, что select должен пройти через список fd, и это медленно. Но почему epoll не должен это делать?
Почему epoll быстрее, чем выбрать?
Ответ 1
Там много дезинформации об этом, но настоящая причина такова:
Типичный сервер может иметь дело, скажем, с 200 соединениями. Он будет обслуживать каждое соединение, которое должно иметь данные, написанные или прочитанные, и тогда ему нужно будет ждать, пока не будет больше работы. Пока он ждет, его нужно прервать, если данные получены по любому из этих 200 подключений.
С select
ядро должно добавить процесс в 200 списков ожидания, по одному для каждого соединения. Для этого ему нужен "thunk", чтобы привязать процесс к списку ожидания. Когда процесс, наконец, просыпается, его нужно удалить из всех 200 списков ожидания, и все эти thunks необходимо освободить.
В отличие от epoll
, у самого сокета epoll
есть список ожидания. Процесс должен быть включен только в один список ожидания, используя только один бит. Когда процесс просыпается, его нужно удалить из одного списка ожидания, и только один thunk должен быть освобожден.
Чтобы быть ясным, с epoll
сам сокет epoll
должен быть присоединен к каждому из этих 200 подключений. Но это делается один раз для каждого соединения, когда оно принято в первую очередь. И это разрывается один раз, для каждого соединения, когда оно удаляется. Напротив, каждый вызов select
, который блокирует, должен добавить процесс в каждую очередь ожидания для каждого контролируемого сокета.
По иронии судьбы, при select
наибольшая стоимость исходит из проверки того, имели ли сокеты, у которых не было активности, какие-либо действия. С epoll
нет необходимости проверять сокеты, у которых не было активности, потому что если у них была активность, они сообщали бы сокет epoll
, когда это действие произошло. В некотором смысле, select
проверяет каждый сокет каждый раз, когда вы вызываете select
, чтобы увидеть, есть ли какая-либо активность в то время как epoll
буровые установки, так что сама активность сокета уведомляет процесс.
Ответ 2
Основное различие между epoll
и select
заключается в том, что в select()
список файловых дескрипторов для ожидания только существует в течение всего одного вызова select()
, а вызывающая задача остается только в сокетах 'очереди ожидания в течение одного вызова. В epoll
, с другой стороны, вы создаете один файловый дескриптор, который агрегирует события из нескольких других файловых дескрипторов, которые вы хотите подождать, и поэтому список отслеживаемых fd является долговечным, а задачи остаются в очереди ожидания сокетов в несколько системных вызовов. Кроме того, поскольку epoll
fd можно использовать для нескольких задач, это больше не одна задача в очереди ожидания, а сама структура, содержащая еще одну очередь ожидания, содержащую все процессы, ожидающие в настоящее время на epoll
fd. (В терминах реализации это абстрагируется очередями ожидания сокетов, содержащих указатель на функцию и указатель данных void*
для передачи этой функции).
Итак, чтобы объяснить механику немного больше:
- Файловый дескриптор
epoll
имеет закрытыйstruct eventpoll
, который отслеживает, какие fd прикреплены к этому fd.struct eventpoll
также имеет очередь ожидания, которая отслеживает все процессы, которые в настоящее времяepoll_wait
ing на этом fd.struct epoll
также имеет список всех файловых дескрипторов, доступных в настоящее время для чтения или записи. - Когда вы добавляете дескриптор файла в
epoll
fd, используяepoll_ctl()
,epoll
добавляетstruct eventpoll
в эту очередь ожидания fd. Он также проверяет, готов ли fd к обработке и добавляет его в готовый список, если это так. - Когда вы ждете на
epoll
fd, используяepoll_wait
, ядро сначала проверяет готовый список и немедленно возвращается, если какие-либо дескрипторы файлов уже готовы. Если нет, он добавляет себя в одиночную очередь ожидания внутриstruct eventpoll
и переходит в режим сна. - Когда событие происходит в сокете
epoll()
ed, он вызывает обратный вызовepoll
, который добавляет дескриптор файла в готовый список, а также пробуждает всех официантов, которые в настоящее время ждут этогоstruct eventpoll
.
Очевидно, что требуется большая тщательная блокировка на struct eventpoll
, а также различные списки и очереди ожидания, но это детализация реализации.
Важно отметить, что ни в какой точке выше я не описывал шаг, который пересекает все представляющие интерес файловые дескрипторы. epoll может избежать использования O (n) времени для операции, где n - количество дескрипторов файлов контролируется.