Книга: UNIX: взаимодействие процессов

Пример

Пример

Переделаем программу, приведенную в листинге 4.1, таким образом, чтобы использовать два канала FIFO вместо двух программных каналов. Функции client и server останутся прежними; отличия появятся только в функции main, новый текст которой приведен в листинге 4.6.

Листинг 4.6. Функция main приложения клиент-сервер, использующего две очереди

//pipe/mainfifo.c
1  #include "unpipc.h"
2  #define FIFO1 "/tmp/fifo.1"
3  #define FIFO2 "/tmp/fifo.2"
4  void client(int, int), server(int. int);
5  int
6  main(int argc, char **argv)
7  {
8   int readfd, writefd;
9   pid_t childpid;
10  /* создание двух FIFO, если существуют – OK */
11  if ((mkfifo(FIF01, FILE_MODE) < 0) && (errno != EEXIST))
12   err_sys("can't create %s", FIF01);
13  if ((mkfifo(FIF02, FILE_MODE) < 0) && (errno != EEXIST)) {
14   unlink(FIF01);
15   err_sys("can't create %s", FIF02);
16  }
17  if ((childpid = Fork()) == 0) { /* child */
18   readfd = Open(FIF01, O_RDONLY, 0);
19   writefd = Open(FIF02, O_WRONLY, 0);
20   server(readfd, writefd);
21   exit(0);
22  }
23  /* родительский процесс */
24  writefd = Open(FIF01, O_WRONLY, 0);
25  readfd = Open(FIF02, O_RDONLY, 0);
26  client(readfd, writefd);
27  waitpid(childpid, NULL, 0); /* ожидание завершения дочернего процесса */
28  Close(readfd):
29  Close(writefd);
30  Unlink(FIF01);
31  Unlink(FIF02);
32  exit(0);
33 }

Создание двух FIFO

10-16 В файловой системе в каталоге /tmp создается два канала. Если какой-либо из них уже существует — ничего страшного. Константа FILE_MODE определена в нашем заголовке unpiрс.h (листинг В.1) как

#define FILEMODE(S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
/* разрешения по умолчанию для вновь создаваемых файлов */

При этом владельцу файла разрешается чтение и запись в него, а группе и прочим пользователям — только чтение. Эти биты разрешений накладываются на маску режима доступа создаваемых файлов (file mode creation mask) процесса.

17-27 Далее происходит вызов fork, дочерний процесс вызывает функцию server (листинг 4.3), а родительский процесс вызывает функцию client (листинг 4.2). Перед вызовом этих функций родительский процесс открывает первый канал на запись, а второй на чтение, в то время как дочерний процесс открывает первый канал на чтение, а второй — на запись. Картина аналогична примеру с каналами и иллюстрируется рис. 4.11. 


Рис. 4.11. Приложение клиент-сервер, использующее две очереди

Изменения по сравнению с примером, в которым использовались программные каналы, следующие:

? Для создания и открытия программного канала требуется только один вызов — pipe. Для создания и открытия FIFO требуется вызов mkfifo и последующий вызов open.

? Программный канал автоматически исчезает после того, как будет закрыт последним использующим его процессом. Канал FIFO удаляется из файловой системы только после вызова unlink. Польза от лишнего вызова, необходимого для создания FIFO, следующая: канал FIFO получает имя в файловой системе, что позволяет одному процессу создать такой канал, а другому открыть его, даже если последний не является родственным первому. С программными каналами это неосуществимо.

В программах, некорректно использующих каналы FIFO, могут возникать неочевидные проблемы. Рассмотрим, например, листинг 4.6: если поменять порядок двух вызовов функции open в породившем процессе, программа перестанет работать. Причина в том, что чтение из FIFO блокирует процесс, если канал еще не открыт на запись каким-либо другим процессом. Действительно, если мы меняем порядок вызовов open в породившем процессе, и породивший, и порожденный процессы открывают канал на чтение, притом что на запись он еще не открыт, так что оба процесса блокируются. Такая ситуация называется блокированием, или зависанием (deadlock). Она будет рассмотрена подробно в следующем разделе.

Оглавление книги


Генерация: 0.109. Запросов К БД/Cache: 0 / 0
поделиться
Вверх Вниз