Выбрать главу
Листинг 4.7. Функция main независимого сервера

//pipe/server_main.c

1  #include "fifo.h"

2  void server(int, int);

3  int

4  main(int argc, char **argv)

5  {

6   int readfd, writefd;

7   /* создание двух FIFO. OK, если они существуют */

8   if ((mkfifo(FIF01, FILE_MODE) < 0) && (errno != EEXIST))

9    err_sys("can't create %s", FIF01);

10  if ((mkfifo(FIF02, FILE MODE) < 0) && (errno != EEXIST)) {

11   unlink(FIF01);

12   err_sys("can't create %s", FIF02);

13  }

14  readfd = Open(FIF01, O_RDONLY, 0);

15  writefd = Open(FIFO2, O_WRONLY, 0);

16  server(readfd, writefd);

17  exit(0);

18 } 

Листинг 4.8. Заголовочный файл fifo.h, используемый и клиентом, и сервером

//pipe/fifo.h

1 #include "unpipc.h"

2 #define FIFO1 "/tmp/fifo.1"

3 #define FIFO2 "/tmp/fifo.2"

Листинг 4.9. Функция main независимого клиента

//pipe/client_main.c

1  #include "fifo.h"

2  void client(int, int);

3  int

4  main(int argc, char **argv)

5  {

6   int readfd, writefd;

7   writefd = Open(FIFO1, O_WRONLY, 0);

8   readfd = Open(FIFO2, O_RDONLY, 0);

9   client(readfd, writefd);

10  Close(readfd);

11  Close(writefd);

12  Unlink(FIFO1);

13  UnLink(FIFO2);

14  exit(0);

15 }

ПРИМЕЧАНИЕ

Для программных каналов и каналов FIFO ядро ведет подсчет числа открытых дескрипторов, относящихся к ним, поэтому безразлично, кто именно вызовет unlink — клиент или сервер. Хотя эта функция и удаляет файл из файловой системы, она не влияет на открытые в момент ее выполнения дескрипторы. Однако для других форм IPC, таких как очереди сообщений стандарта System V, счетчик отсутствует, и если сервер удалит очередь после записи в нее последнего сообщения, она может быть удалена еще до того, как клиент это сообщение считает.

Для запуска клиента и сервера запустите сервер в фоновом режиме:

% server_fifo &

а затем запустите клиент. Можно было сделать и по-другому: запускать только программу-клиент, которая запускала бы сервер с помощью fork и exec. Клиент мог бы передавать серверу имена FIFO в качестве аргументов командной строки в команде exec, вместо того чтобы обе программы считывали их из заголовка. Но в этом случае сервер являлся бы дочерним процессом и проще было бы обойтись программным каналом.

4.7. Некоторые свойства именованных и неименованных каналов

Некоторые свойства именованных и неименованных каналов, относящиеся к их открытию, а также чтению и записи данных, заслуживают более пристального внимания. Прежде всего можно сделать дескриптор неблокируемым двумя способами. 

1. При вызове open указать флаг O_NONBLOCK. Например, первый вызов open в листинге 4.9 мог бы выглядеть так:

writefd = Open(FIFO1, O_WRONLY | O_NONBLOCK, 0);

2. Если дескриптор уже открыт, можно использовать fcntl для включения флага O_NONBLOCK. Этот прием нужно применять для программных каналов, поскольку для них не вызывается функция open и нет возможности указать флаг O_NONBLOCK при ее вызове. Используя fcntl, мы сначала получаем текущий статус файла с помощью F_GETFL, затем добавляем к нему с помощью побитового логического сложения (OR) флаг O_NONBLOCK и записываем новый статус с помощью команды F_SETFL:

int flags;

if ((flags = fcntl(fd, F_GETFL, 0)) < 0) err_sys("F_GETFL error");

flags |= O_NONBLOCK;

if (fcntl(fd, F_SETFL, flags) < 0) err_sys("F_SETFL error");

Будьте аккуратны с программами, которые просто устанавливают требуемый флаг, поскольку при этом сбрасываются все прочие флаги состояния:

/* Неправильное отключение блокировки */

if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) err_sys("F_SETFL error");

Таблица 4.1 иллюстрирует действие флага, отключающего блокировку, при открытии очереди и при чтении данных из пустого программного канала или канала FIFO.

Таблица 4.1. Действие флага O_NONBLOCK на именованные и неименованные каналы 

Операция Наличие открытых каналов Блокировка включена (по умолчанию) Флаг O_NONBLOCK установлен
Открытие (open) FIFO только для чтения FIFO открыт на запись Возвращается код успешного завершения операции Возвращается код успешного завершения операции
Открытие (open) FIFO только для чтения FIFO не открыт на запись Процесс блокируется, пока FIFO не будет открыт на запись Возвращается код успешного завершения операции
Открытие (open) FIFO только для записи FIFO открыт на чтение Возвращает код успешного завершения операции Возвращает код успешного завершения операции
Открытие (open) FIFO только для записи FIFO не открыт на чтение Блокируется до тех пор, пока FIFO не будет открыт на чтение Возвращает ошибку с кодом ENXIO
Чтение (read) из пустого программного канала или FIFO Программный канал или FIFO открыт на запись Блокируется до тех пор, пока в программный канал или FIFO не будут помещены данные или они не будут закрыты всеми процессами, которыми они были открыты на запись Возвращает ошибку с кодом EAGAIN
Чтение (read) из пустого программного канала или FIFO Программный канал или FIFO не открыт на запись read возвращает 0 (конец файла) read возвращает 0 (конец файла)
Запись (write) в программный канал или FIFO Программный канал или FIFO открыт на чтение (См. в тексте) (См. в тексте)
Запись (write) в программный канал или FIFO Программный канал или FIFO не открыт на чтение Программному потоку посылается сигнал SIGPIPE Программному потоку посылается сигнал SIGPIPE