Троан Эрик В.
Шрифт:
9: int fds[2];
10: char buf[4096];
11: int i;
12: int fd;
13:
14: /* открыть оба канала в неблокирующем режиме */
15: if ((fds[0] = open("p1", O_RDONLY | O_NONBLOCK)) < 0) {
16: perror("open p1");
17: return 1;
18: }
19:
20: if ((fds[1] = open("p2", O_RDONLY | O_NONBLOCK)) < 0) {
21: perror("open p2");
22: return 1;
23: }
24:
25: fd = 0;
26: while (1) {
27: /* если данные доступны, прочитать и отобразить их */
28: i = read(fds[fd], buf, sizeof (buf) - 1);
29: if ((i < 0) && (errno ! = EAGAIN)) {
30: perror("read");
31: return 1;
32: } else if (i > 0) {
33: buf[i] = '\0';
34: printf("чтение: %s", buf);
35: }
36:
37: /* читать из другого файлового дескриптора */
38: fd = (fd + 1) % 2;
39: }
40: }
Важное различие между
mpx-nonblock
и mpx-blocks
состоит в том, что программа mpx-nonblock
не закрывается, когда один из каналов, из которого она считывает, закрыт. Неблокируемый read
из канала без записывающих устройств возвращает 0 байт, из канала с таковыми, но без доступных данных read
возвращает EAGAIN
. Простое переключение неблокируемого ввода-вывода между дескрипторами файлов достается высокой ценой. Программа всегда опрашивает два файловых дескриптора для ввода — она никогда не блокируется. Постоянная работа программы приносит системе массу проблем, поскольку операционная система не может перевести процесс в режим ожидания (попробуйте запустить 10 копий
mpx-nonblock
в своей системе и посмотрите, как это скажется на ее производительности). 13.1.2. Мультиплексирование с помощью
poll
Для эффективного мультиплексирования Linux предоставляет системный вызов
poll
, позволяющий процессу блокировать одновременно несколько файловых дескрипторов. Постоянно проверяя каждый файловый дескриптор, процесс создает отдельный системный вызов, определяющий, из каких файловых дескрипторов процесс будет читать, а на какие — записывать. Когда один или несколько таких файлов имеют данные, доступные для чтения, или могут принимать данные, записываемые в них, poll
завершается, и приложение может считывать и записывать данные в дескрипторах, не беспокоясь о блокировке. После обработки этих файлов процесс создает еще один вызов poll
, блокируемый до готовности файла. Ниже показано определение poll
. #include <sys/poll.h>
int poll(struct pollfd * fds, int numfds, int timeout);
Последние два параметра очень просты;
numfds
задает количество элементов в массиве, на который указывает первый параметр, a timeout
определяет, насколько долго poll
должна ожидать события. Если в качестве тайм-аута задается 0, poll
никогда не входит в состояние тайм-аута. Первый параметр,
fds
, описывает, какие файловые дескрипторы следует контролировать, и для каких типов ввода-вывода. Это указатель на массив структур struct pollfd
. struct pollfd {
int fd; /* файловый дескриптор */
short events; /* ожидаемые события ввода-вывода */
short revents; /* происшедшие события ввода-вывода */
};
Первый элемент,
fd
, является контролируемым файловым дескриптором, а элемент events описывает, какие типы событий подлежат мониторингу. Последний представляет собой один или несколько перечисленных флагов, объединенных с помощью логического "ИЛИ". POLLIN | Нормальные данные доступны для считывания из файлового дескриптора. |
POLLPRI | Приоритетные (внешние) данные доступны для считывания. |
POLLOUT | Файловый дескриптор может принимать записываемые на него данные. |
Элемент
revents
структуры struct pollfd
заполняется системным вызовом poll
и отражает состояние файлового дескриптора fd
. Это похоже на элемент events
, но вместо определения интересующих приложение событий ввода-вывода он определяет доступные такие типы. Например, если приложение контролирует канал как для чтения, так и для записи (events
установлено в POLLIN | POLLOUT
), после успешного вызова poll
в revents
устанавливается бит POLLIN
, если канал готов для чтения, и бит POLLOUT
, если в канале имеется пространство для записи дополнительных данных. Если верно и то, и другое, устанавливаются оба бита.