Листинг 4.4. Двусторонняя связь через двусторонний канал
//pipe/fduplex.c
1 #include "unpipc.h"
2 int
3 main(int argc, char **argv)
4 {
5 int fd[2], n;
6 char c;
7 pid_t childpid;
8 Pipe(fd); /* предполагается двусторонний канал (напр., SVR4) */
9 if ((childpid = Fork()) == 0) { /* child */
10 sleep(3):
11 if ((n = Read(fd[0], &c, 1)) != 1)
12 err_quit("child: read returned %d", n);
13 printf("child read %c\n", c):
14 Write(fd[0], "c", 1);
15 exit(0);
16 }
17 /* родитель */
18 Write(fd[1], "p", 1);
19 if ((n = Read(fd[1], &c, 1)) != 1)
20 err_quit("parent: read returned %d", n):
21 printf("parent read %c\n", c);
22 exit(0);
23 }
В этой программе сначала создается двусторонний канал, затем делается системный вызов fork. Породивший процесс записывает символ р в канал, а затем считывает из канала данные. Дочерний процесс ждет три секунды, считывает символ из канала, а потом записывает туда символ с. Задержка чтения для дочернего процесса позволяет породившему процессу вызвать read первым — таким образом мы можем узнать, не будет ли им считан обратно только что записанный символ.
При запуске этой программы в Solaris 2.6, в которой организована поддержка двусторонних каналов, мы получим ожидаемый результат:
solaris % fduplex
child read p
parent read с
Символ р передается по одному из двух односторонних каналов, изображенных на рис. 4.10, а именно по верхнему каналу. Символ с передается по нижнему одностороннему каналу. Родительский процесс не считывает обратно записанный им в канал символ р (что и требуется).
При запуске этой программы в Digital Unix 4.0B, в которой по умолчанию создаются односторонние каналы (двусторонние каналы — как в SVR4 — будут создаваться в том случае, если при компиляции указать специальные параметры), мы увидим результат, ожидаемый для одностороннего канала:
alpha % fduplex
read error: Bad file number
alpha % child read p
write error: Bad file number
Родительский процесс записывает символ р, который успешно считывается дочерним процессом, однако при попытке считывания из канала (дескриптор fd[l]) родительский процесс прерывается с ошибкой, как и дочерний процесс, при попытке записи в канал (дескриптор fd[0]). Вспомните рис. 4.8. Функция read возвращает код ошибки EBADF, означающий, что дескриптор не открыт для чтения. Аналогично write возвращает тот же код ошибки, если дескриптор не был открыт на запись.