Главная страница Взаимодействие нетривиальных процессов 14 WriteCwritefd. buff, len); 15 /* считывание из канала, вывод в stdout */ 16 while ( (n = ReadCreadfd, buff. MAXLINE)) > 0) 17 WriteCSTDOUT FILENO, buff, n): 18 } Считывание полного имени из стандартного потока ввода 8-14 Полное имя файла считывается из стандартного потока ввода и записывается в канал после удаления завершающего символа перевода строки, возвращаемого функцией fgets. Копирование из канала в стандартный поток вывода 15-17 Затем клиент считывает все, что сервер направляет в канал, и записывает эти данные в стандартный поток вывода. Ожидается, что это будет содержимое файла, но в случае его отсутствия будет принято и записано в стандартный поток вывода сообщение об ошибке. В листинге 4.3 приведена функция server. Листинг 4.3. Функция server для приложения клиент-сервер с двумя каналами pipe/server,с 1 #include unpipc.h 2 void 3 serverCint readfd, int writefd) 5 int fd: 6 ssize t n; 7 char buff[MAXLINE+l]: 8 /* получение полного имени из канала IPC */ 9 if ( (n = ReadCreadfd. buff. MAXLINE)) == 0) 10 err quit( end-of-file while reading pathname ): 11 buff[n] = \0; /* полное имя завершается О */ 12 if ( (fd = openCbuff, 0 ROONLY)) < 0) { 13 /* 4error: must tell client */ 14 snprintf(buff + n, sizeof(buff) - n. : cant open, *s\n . 15 strerror(errno)): 16 n - strlen(buff): 17 WriteCwritefd. buff, n): 18 } else { 19 /* файл успешно открыт и копируется в канал */ 20 while ( (п = ReadCfd, buff. MAXLINE)) > 0) 21 WriteCwritefd, buff, n): 22 Close(fd): 23 } 24 } Считывание полного имени файла из канала 8-11 Записанное в канал клиентом имя файла считывается сервером и дополняется завершающим символом с кодом О (null-terminated). Обратите внимание, что write mi] П<ойносторонний канал х 1 fd[0] Рис. 4.8. Односторонний канал Двусторонний канал мог бы быть реализован так, как это изображено на рис. 4.9. В этом случае неявно предполагается существование единственного буфера, в который помещается все, что записывается в канал (с любого конца, то есть дескриптора), и при чтении из канала данные просто считываются из буфера. write read двусторонний канал read write Рис. 4.9. Одна из возможных реализаций двустороннего канала (неправильная) функция read возвращает данные, как только они помещаются в поток, не ожидая накопления некоторого их количества (MAXLINE в данном примере). Открытие файла, обработка возможной ошибки 12-17 Файл открывается для чтения и при возникновении ошибки сообщение о ней возвращается клиенту с помощью канала. Для получения строки с соответствующим значению переменной еггпо сообщением об ошибке вызывается функция strerror (в книге [24, с. 690-691] вы найдете более подробный рассказ об этой функции). Копирование из файла в канал 18-23 При успешном завершении работы функции open содержимое файла копируется в канал. Ниже приведен результат работы программы в случае наличия файла с указанным полным именем и в случае возникновения ошибок: Solaris % mainpipe /etc/inet/ntp.conf файл, состоящий из двух строк multicastclient 224.0.1.1 driftfile /etc/inet/ntp.drift Solaris % mainpipe /etc/shadow файл, на чтение которого нет разрешения /etc/shadow; cant open. Permission denied Solaris % mainpipe /no/such/file несуществующий файл /по/such/file: cant open. No such file or directory 4.4. Двусторонние каналы в предыдущем разделе мы отметили, что во многих системах реализованы двусторонние каналы. В Unix SVR4 это обеспечивается самой функцией pipe, а во многих других ядрах - функцией socketpair. Но что в действительности представляет собой двусторонний канал? Представим себе сначала однонаправленный канал, изображенный на рис. 4.8. Такая реализация вызовет проблемы, например, в программе листинга А.14. Здесь требуется двусторонняя передача информации, причем потоки данных должны быть независимы, В противном случае некоторый процесс, записав данные в канал и перейдя затем в режим чтения из этого же канала, рискует считать обратно те же данные, которые были им только что туда записаны. На рис. 4.10 изображена правильная реализация двустороннего канала. X односторонний канал х >fd[1] X односторонний канал X -jjg fd[0] Рис. 4.10. Правильная реализация двустороннего канала Здесь двусторонний канал получается из объединения двух односторонних. Все данные, записываемые в fd[l], будут доступны для чтения из fd[0], а данные, записываемые в fd[0], будут доступны для чтения из fd[l]. Программа в листинге 4.4 иллюстрирует использование одного двустороннего канала для двусторонней передачи информации. Листинг 4.4. Двусторонняя связь через двусторонний канал pipe/fduplex.c 1 #include unpipc.h 2 int 3 mainCint argc. char **argv) 5 int fd[2], n; 6 char c; 7 pid t childpid; 8 PipeCfd); /* предполагается двусторонний канал Снапр.. SVR4) */ 9 if С Cchildpid - ForkO) - 0) { /* child */ 10 sleepC3); 11 if С Cn - ReadCfdCO]. Sc. D) J- 1) 12 err quitC child: read returned *d . n); 13 printfC child read %c\n . c); 14 WriteCfd[0], c , 1); 15 exitCO); 16 } 17 /* родитель */ 18 WriteCfd[l], p . 1): 19 if С Cn - ReadCfd[l]. &c, D) !- 1) 20 err quitС parent: read returned *d , n); 21 printfС parent read *c\n , c): 22 exitCO): 23 } В этой программе сначала создается двусторонний канал, затем делается системный вызов fork. Породивший процесс записывает символ р в канал, а затем считывает из канала данные. Дочерний процесс ждет три секунды, считывает символ из канала, а потом записывает туда символ с. Задержка чтения для дочернего процесса позволяет породившему процессу вызвать read первым - таким обра-
|
© 2000 - 2024 ULTRASONEX-AMFODENT.RU.
Копирование материалов разрешено исключительно при условии цититирования. |