1: /* mpx-blocks.с */
2:
3: #include
4: #include
5: #include
6:
7: int main(void) {
8: int fds[2];
9: char buf[4096];
10: int i;
11: int fd;
12:
13: if ((fds[0] = open("p1", O_RDONLY) ) < 0) {
14: perror("open p1");
15: return 1;
16: }
17:
18: if ( (fds[1] = open("p2", O_RDONLY)) < 0) {
19: perror("open p2");
20: return 1;
21: }
22:
23: fd = 0;
24: while (1) {
25: /* если данные доступны, прочитать и отобразить их */
26: i = read (fds[fd], buf, sizeof (buf) - 1);
27: if (i < 0) {
28: perror("read");
29: return 1;
30: } else if (!i) {
31: printf("канал закрыт\n");
32: return 0;
33: }
34:
35: buf[i] = '\0';
36: printf ("чтение: %s", buf);
37:
38: /* читать из другого файлового дескриптора */
39: fd = (fd + 1) % 2;
40: }
41: }
Хотя программа mpx-blocks
может считывать одновременно из обоих каналов, это не является особо эффективным. Она считывает из каждого канала по очереди. После запуска программа читает из первого файла, пока в нем доступны данные, второй файл игнорируется вплоть до возврата из read()
для первого файла. Как только произошел возврат, первый файл игнорируется вплоть до чтения данных из второго файла. Этот метод не поддерживает гладкое мультиплексирование данных. На рис. 13.1 показана программа mpx-blocks
во время выполнения.
Рис. 13.1. Примеры запуска мультиплексной передачи
13.1.1. Неблокируемый ввод-вывод
Как упоминалось в главе 11, неблокируемый файл можно определить с помощью системного вызова fcntl
. Если медленный файл неблокируемый, read()
сразу же возвращается. Если данные недоступны, она просто возвращает 0. Неблокируемый ввод- вывод предоставляет простое решение мультиплексирования, предотвращая блокирование файловых операций.
Показанная ниже модифицированная версия mpx-blocks
пользуется преимуществом неблокируемого ввода-вывода для более гладкого переключения между p1
и p2
.
1: /* mpx-nonblock.c */
2:
3: #include
4: #include
5: #include
6: #include
7:
8: int main(void) {
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()
.
Читать дальше