Как заменить select() на kevent() для повышения производительности?

Со страницы Kqueue в Википедии:

Kqueue обеспечивает эффективные конвейеры ввода и вывода событий между ядром и пользовательской средой. Таким образом, можно изменять фильтры событий, а также получать ожидающие события, используя только один системный вызов kevent(2) на итерацию основного цикла событий. Это контрастирует со старыми традиционными системными вызовами опроса, такими как poll(2) и select(2), которые менее эффективны, особенно при опросе событий для большого количества файловых дескрипторов

Звучит здорово. Я ориентируюсь на FreeBSD для своего сервера, и я обрабатываю значительное количество fd сетевых сокетов — используя select() для них всех и выясняя, у кого считывать данные. Я бы предпочел использовать вызовы kevent() для повышения производительности, поскольку именно для этого он и существует!

Я прочитал справочную страницу для kevent во FreeBSD здесь но для меня это загадочно, и я не нахожу хороших ресурсов, объясняющих это. Пример использования kevent вместо select решит мою проблему, а также поможет лучше понять, как используется kevent().


person Nektarios    schedule 22.04.2011    source источник


Ответы (2)


Сначала создайте новую очередь:

int kq=kqueue();

Теперь зарегистрируйте свой fd в kq:

struct kevent kev;
kev.ident=your_fd;
kev.flags=EV_ADD | EV_CLEAR;
kev.filter=EVFILT_READ;
kev.fflags=0;
kev.data=0;
kev.udata=&your_data;

int res=kevent(kq,&kev,1,0,0,0);

Наконец, подождите, пока данные поступят в ваш сокет:

struct kevent res_kevs[5];
int res=kevent(kq,0,0,res_kevs,5,0);

После возврата res_kevs[i].ident будет содержать дескриптор вашего сокета, res_kevs[i].data - количество байтов, готовых к чтению.

См. man kevent для получения более подробной информации и функций.

person arrowd    schedule 25.04.2011
comment
спасибо за то, что вы были первым, кто действительно ответил на мой вопрос. Просто для уточнения - у меня будет kevent для каждого из моих fd (тысячи) и один kqueue для всего их набора? Это так обычно делается? - person Nektarios; 25.04.2011
comment
Да, потому что каждый вызов kevent() ожидает только одну очередь kqueue. Тем не менее, вы можете добавить дескриптор kqueue в другую kqueue, заполнив kev.ident kq. Я не уверен, как именно это работает, потому что по какой-то причине это не задокументировано. - person arrowd; 26.04.2011

Обычно вы используете libevent, который позаботится обо всех деталях за вас, а также означает, что вы можете перенести свою программу на другую ОС с другой схемой (например, Linux и epoll) для выполнения чего-то подобного.

person MarkR    schedule 22.04.2011
comment
Но я уже делаю все, что делает эта библиотека, что меня волнует, кроме использования kevent для проверки, читаю ли я из fds или нет. Я бы не хотел переписывать свою кодовую базу, чтобы использовать эту библиотеку только для этого, хотя, если бы я начинал, возможно, я бы так и сделал. Кроме того, я съеживаюсь от чего-то, размещенного на monkey.org. - person Nektarios; 22.04.2011
comment
Определенно не хотел бы и суперсет! Хотя я могу посмотреть на эти части программного обеспечения и собрать ответ, я думаю, но на самом деле это не касается исходного вопроса. - person Nektarios; 23.04.2011
comment
@Nektarios: Конечно, он отвечает на него. Если вы используете одну из этих библиотек в системе BSD, она будет использовать kevent, а не select. - person Zan Lynx; 25.04.2011