Значение flags | Описание | Изменяется | Возвращается |
EV_ADD | Добавить новое событие, подразумевается по умолчанию, если не указан флаг EV_DISABLE | • | |
EV_CLEAR | Сброс состояния события после считывания его пользователем | • | |
EV_DELETE | Удаление события из фильтра | • | |
EV_DISABLE | Отключение события без удаления его из фильтра | • | |
EV_ENABLE | Включение отключенного перед этим события | • | |
EV_ONESHOT | Удаление события после его однократного срабатывания | • | |
EV_EOF | Достигнут конец файла | | • |
EV_ERROR | Произошла ошибка, код errno записан в поле data | | • |
Типы фильтров приведены в табл. 14.6.
Таблица 14.6. Типы фильтров
Значение filter | Описание |
EVFILT_AIO | События асинхронного ввода-вывода |
EVFILT_PROC | События exit, fork, exec для процесса |
EVFILT_READ | Дескриптор готов для чтения (аналогично select) |
EVFILT_SIGNAL | Описание сигнала |
EVFILT_TIMER | Периодические или одноразовые таймеры |
EVFILT_VNODE | Изменение и удаление файлов |
EVFILT_WRITE | Дескриптор готов для записи (аналогично select) |
Перепишем функцию
str_cli
из листинга 6.2 так, чтобы она использовала
kqueue
. Результат представлен в листинге 14.8.
Листинг 14.8. Функция str_cli, использующая kqueue
//advio/str_cli_kqueue04.c
1 #include "unp.h"
2 void
3 str_cli(FILE *fp, int sockfd)
4 {
5 int kq, i, n, nev, stdineof = 0, isfile;
6 char buf[MAXLINE];
7 struct kevent kev[2];
8 struct timespec ts;
9 struct stat st;
10 isfile = ((fstat(fileno(fp), &st) 0) &&
11 (st.st_mode & S_IFMT) == S_IFREG);
12 EV_SET(&kev[0], fileno(fp), EVFILT_READ, EV_ADD, 0, 0, NULL);
13 EV_SET(&kev[1], sockfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
14 kq = Kqueue();
15 ts.tv_sec = ts.tv_nsec = 0;
16 Kevent(kq, kev, 2, NULL, 0, &ts);
17 for (;;) {
18 nev = Kevent(kq, NULL, 0, kev, 2, NULL);
19 for (i = 0; i < nev; i++) {
20 if (kev[i].ident == sockfd) { /* сокет готов для чтения */
21 if ((n = Read(sockfd, buf, MAXLINE)) == 0) {
22 if (stdineof == 1)
23 return; /* нормальное завершение*/
24 else
25 err_quit("str_cli: server terminated prematurely");
26 }
27 Write(fileno(stdout), buf, n);
28 }
29 if (kev[i].ident == fileno(fp)) { /* входной поток готов к чтению */
30 n = Read(fileno(fp), buf, MAXLINE);
31 if (n > 0)
32 Writen(sockfd, buf, n);
33 if (n == 0 || (isfile && n == kev[i].data)) {
34 stdineof = 1;
35 Shutdown(sockfd, SHUT_WR); /* отправка FIN */
36 kev[i].flags = EV_DELETE;
37 Kevent(kq, &kev[i], 1, NULL, 0, &ts); /* удаление
kevent */
38 continue;
39 }
40 }
41 }
42 }
43 }
Проверка, указывает ли дескриптор на файл
10-11
Поведение
kqueue
при достижении конца файла зависит от того, связан ли данный дескриптор с файлом, каналом или терминалом, поэтому мы вызываем
fstat
, чтобы убедиться, что мы работаем с файлом. Эти сведения понадобятся позже.
Настройка структур kevent для kqueue
12-13
При помощи макроса
EV_SET
мы настраиваем две структуры
kevent
. Обе содержат фильтр событий готовности к чтению (
EVFILT_READ
) и запрос на добавление этого события к фильтру (
EV_ADD
).