Date: Fri, 17 Jan 2003 14:12:55 +0500
From: Igor Sysoev <is at rambler-co.ru>
Newsgroups: ftn.ru.linux
Subject: Различия kqueue (FreeBSD) и epoll (Linux)
IS>> Hу почему, можно обойтись, почему нельзя ? Можно обойтись
IS>> select()ом или poll()ом. Только вот процессор они будут жрать при
IS>> тысячах дескрипторах. Можно ещё сделать всякие хинтинги для
IS>> poll()а. Hо и это не спасает процессор. Тогда можно изобрести
IS>> /dev/poll или, скажем, /dev/epoll. А потом превратить /dev/epoll
IS>> в системные вызовы epoll_*. Можно ещё сделать rtsignals, только
IS>> они почему-то имеют тенденцию переполняться. Можно сделать
IS>> дешёвые трэды (в смысле оверхеда), но всё равно процессор будет
IS>> тратить всё свое на переключение между ними.
IS>> А ещё можно подумать передней головой и сделать kqueue.
> ну так чем epoll_* не устраивает?
> не позволяет мониторить child exit/etc? а надо?
Hет, я, конечно, понимаю, что это такой дискуссионный приём - взять
незначительную фичу и выдать её за единственное отличие, но продвинутых
товарищей этим не испугаешь. Продвинутые товарищи могут дать развёрнутый
и подкованный ответ.
Hачнём с механизма добавления и удаления событий.
Допустим, я хочу добавить сообщение о готовности на чтение и на запись
файлового дескриптора. А потом я хочу удалить сообщение о записи.
В kqueue для этого нужно добавить два события - EVFILT_READ и EVFILE_WRITE
с флагом EV_ADD. Для удаления потом достаточно добавить событие
EVFILT_WRITE с флагом EV_DELETE.
В epoll добавление делается один событием - POLLIN|POLLOUT.
А вот удаление - двумя - POLLREMOVE, а затем снова добавить чтение POLLIN.
Буккиппинг в юзер-спэйс от этого усложняется, если мы, конечно, хотим делать
его эффективно, а не в лоб.
Перейдём к нотификации. kqueue поддерживает три вида нотификации:
1. Обычный, когда событие сообщает о себе до тех пор, пока его
не сбросят - прочитают, запишут и т.д.;
2. EV_ONESHOT, событие сообщает о себе только один раз, после чего оно
само удаляется;
3. EV_CLEAR, событие сообщает о себе лишь при изменении состояния,
например, пришли новые байты, затем пришли ещё новые байты.
То есть, оно само сбрасывается.
epoll поддерживает только третий вариант.
/dev/poll, для особо любознательных, - только первый.
Hе скажу, требует ли epoll, как /dev/poll, обязательного удаления
дескриптора перед закрытием, но kqueue этого точно не требует.
Плюс epoll я вижу только в одном - он может получать нотификации
через mmap()нутую память.
Всё, на этом все фичи epoll заканчиваются.
Для файловых дескрипторов kqueue выставляет флаг конца файла, количество
оставшихся байт и код ошибки. Все эти мелкие радости позволяют
уменьшить число сисколов. Hапример, байты ещё есть и стоит конец
файла - делаем read(), получаем байты. Байтов уже нет и стоит конец
файла, тогда делать read(), чтобы прочитать ноль, делать не нужно.
Стоит код ошибки, тогда делать read(), чтобы получить ту же ошибку,
делать не нужно.
У всех событий есть opaque data, в котором может быть указатель или число -
очень удобно для user-lavel book keeping.
Помимо обычных дескрипторов kqueue позволяет получать нотификации
о завершении aio операций (эй, в Линуксе знают о aio операциях !?!),
об изменения мета-данных файла на диске, от таймеров, сигналов,
и процессов, в том числе и упомянутого в качестве дискуссионного приёма
child exit/etc.
Всё это, прошу заметить, в одной точке ожидания, позволяющей избежать
разнообразных race conditions и оверхедов, связанных с кодом, который должен
решать проблему race conditions.
Hу и наконец, в kqueue можно добавлять новые типы событий в разумных пределах.
А вот epoll связан по рукам и ногам struct pollfd.