Троан Эрик В.
Шрифт:
Существует несколько битов, которые ядро может установить в
revents
, но которые невозможно установить в events
. POLLERR | В дескрипторе файла имеется ожидающая ошибка; выполнение системного вызова на файловом дескрипторе приведет к установке errno в подходящий код. |
POLLHUP | Файл был отключен; в него больше невозможно ничего записывать (хотя могут остаться данные для считывания). Это происходит в случае отключения терминала либо закрытия удаленного конца канала или сокета. |
POLLNVAL | Файловый дескриптор недоступен (он не относится к открытому файлу). |
Возвращаемое значение
poll
равно нулю в случае тайм-аута вызова, -1 в случае ошибки (например, fds
— неверный указатель; ошибки в самих файлах вызывают установку POLLERR
), или же положительное число, описывающее количество файлов с ненулевыми элементами revents. В отличие от неэффективного метода мультиплексирования входных и выходных данных из каналов, используемого ранее,
poll
довольно легко решает ту же проблему. Применяя poll
к файловым дескрипторам одновременно для обоих каналов, мы знаем, что когда poll
возвращается, один из каналов готов для чтения либо закрыт. Мы проверяем элемент revents
для обоих файловых дескрипторов, чтобы узнать, какие действия предпринять, и по завершении возвращаемся в вызов poll
. Теперь большая часть времени тратится на блокирование вызова poll
, а не на постоянную проверку файловых дескрипторов, использующих неблокируемый ввод-вывод, что значительно уменьшает нагрузку на систему. Ниже показан код mpx-poll
. 1: /* mpx-poll.с */
2:
3: #include <fcntl.h>
4: #include <stdio.h>
5: #include <sys/poll.h>
6: #include <unistd.h>
7:
8: int main(void) {
9: struct pollfdfds[2];
10: char buf [4096];
11: int i, rc;
12:
13: /* открыть оба канала */
14: if ( (fds[0].fd = open("p1", O_RDONLY | O_NONBLOCK)) < 0) {
15: perror("open p1");
16: return 1;
17: }
18:
19: if ((fds[1].fd = open("p2", O_RDONLY | O_NONBLOCK)) < 0) {
20: perror("open p2");
21: return 1;
22: }
23:
24: /* начать чтение из обоих файловых дескрипторов */
25: fds[0].events = POLLIN;
26: fds[1].events = POLLIN;
27:
28: /* пока наблюдаем за одним из fds[0] или fds[1] */
29: while (fds[0].events || fds[1].events ) {
30: if (poll(fds, 2, 0) < 0) {
31: perror("poll");
32: return 1;
33: }
34:
35: /* проверить, какой из файловых дескрипторов
36: готов для чтения из него */
37: for (i = 0; i < 2; i++) {
38: if (fds[i].revents) {
39: /* fds[i] готов для чтения, двигаться дальше... */
40: rc = read(fds[i].fd, buf, sizeof(buf) - 1);
41: if (rc < 0) {
42: perror("read");
43: return 1;
44: } else if (!rc) {
45: /* этот канал закрыт, не пытаться
46: читать из него снова */
47: fds[i].events = 0;
48: } else {
49: buf[rc] = '\0';
50: printf("чтение : %s", buf);
51: }
52: }
53: }
54: }
55:
56: return 0;
57: }
13.1.3. Мультиплексирование с помощью
select
Системный вызов
poll
был изначально представлен как часть Unix-дерева System V. Усилиями разработчиков BSD та же основная проблема была решена похожим способом — предоставлением системного вызова select
. #include <sys/select.h>
int select(int numfds, fd_set * readfds, fd_set * writefds,
fd_set * exceptfds, struct timeval * timeout);
Три промежуточных параметра —
readfds
, writefds
и exceptfds
— определяют, за какими файловыми дескрипторами необходимо следить. Каждый параметр — это указатель на fd_set
, структуру данных, позволяющую процессу определить произвольное количество файловых дескрипторов [74] . Ею манипулируют с помощью перечисленных ниже макросов.74
Это похоже на тип
sigset_t
, используемый для шаблонов сигналов.