33 if ((rcount = read(pipefd[0], buf, BUFSIZ)) != wcount) {
34 fprintf(stderr, "%s: read failed: %s\n", argv[0],
35 strerror(errno));
36 exit(1);
37 }
38
39 buf[rcount] = '\0';
40
41 printf("Read <%s> from pipe\n", buf);
42 (void)close(pipefd[0]);
43 (void)close(pipefd[1]);
44
45 return 0;
46 }
Строки 11–15 объявляют локальные переменные; наибольший интерес представляет mesg
Строки 17–21 создают канал с проверкой ошибок; строки 23–24 выводят значения новых дескрипторов файлов (просто для подтверждения, что они не равны 0, 1 или 2)
В строке 26 получают длину сообщения для использования с write()
Строки 33–37 считывают содержимое канала, опять с проверкой ошибок. Строка 39 предоставляет завершающий нулевой байт, так что прочитанные данные могут использоваться в качестве обычной строки. Строка 41 выводит данные, а строки 42–43 закрывают оба конца канала. Вот что происходит при запуске программы:
$ ch09-pipedemo
Read end = fd 3, write end = fd 4
Read
Эта программа не делает ничего полезного, но она демонстрирует основы. Обратите внимание, что нет вызовов open()
creat()
и что программа не использует три своих унаследованных дескриптора. Тем не менее, write()
и read()
завершаются успешно, показывая, что дескрипторы файлов действительны и что данные, поступающие в канал, действительно выходят из него.[95] Конечно, будь сообщение слишком большим, наша программа не работала бы. Это происходит из-за того, что размер (памяти) каналов ограничен, факт, который мы обсудим в следующем разделе.Подобно другим дескрипторам файлов, дескрипторы для каналов наследуются порожденным процессом после fork
exec
. Вскоре мы увидим, как использовать это обстоятельство и сделать с каналами что-то интересное.9.3.1.2. Буферирование каналов
Каналы
Когда канал полон, система автоматически
write()
. Когда канал освобождается, система копирует данные в канал, а затем позволяет системному вызову write()
вернуться к производителю.Подобным же образом, если канал пустой, потребитель блокируется в read()
Когда производитель вызывает на записывающем конце канала close()
read()
возвращают 0, указывая на конец файла.Напротив, если потребитель закрывает читаемый конец, write()
Нашей любимой аналогией для каналов является то, как муж и жена вместе моют и сушат тарелки. Один супруг моет тарелки, помещая чистые, но влажные тарелки в сушилку на раковине. Другой супруг вынимает тарелки из сушилки и вытирает их. Моющий тарелки является производителем, сушилка является каналом, а вытирающий является потребителем.[96]
Если вытирающий супруг оказывается быстрее моющего, сушилка становится пустой, и вытирающему приходится ждать, пока не будут готовы новые тарелки. Напротив, если быстрее вытирающий супруг, сушилка наполняется, и моющему приходится ждать, пока она не опустеет, прежде чем помещать в нее тарелки. Это изображено на рис. 9.3.
Рис. 9.3
. Синхронизация процессов канала9.3.2. Очереди FIFO
Для традиционных каналов единственным способом для двух различных программ получить доступ к одному и тому же каналу является наследование дескрипторов файлов. Это означает, что процессы должны быть порожденными от общего родителя или один должен быть предком другого.