Значение 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).


Перейти на страницу:
Изменить размер шрифта: