next up previous contents
Next: Блокировка файлов. Up: Трубы (pipes) Previous: Функция popen().   Contents

FIFO - именованные каналы.

С помощью труб могут общаться только родственные друг другу процессы, полученные с помощью fork(). Именованные каналы FIFO дают возможность обмена данными с абсолютно чужим процессом.

С точки зрения ядра ОС FIFO является одним из вариантов реализации трубы. Системный вызов mkfifo() предоставляет именованную трубу в виде объекта файловой системы. Как и для любого другого объекта, необходимо предоставлять процессам права доступа в FIFO, чтобы определить, кто может писать что-либо в FIFO, и кто может читать из нее. Несколько процессов могут записывать или читать FIFO одновременно. Режим работы с FIFO - полудуплексный, т.е. процессы могут общаться в одном направлении. Типичное применение FIFO - разработка приложений клиент-сервер.

Синтаксис функции для создания FIFO следующий:

#include <sys/types.h>

#include <sys/stat.h>

int mkfifo(const char *fifoname, mode_t mode);

При возникновении ошибки функция возвращает -1, в противном случае 0. В качестве первого параметра указывается путь, где будет располагаться FIFO. Второй параметр определяет режим работы с FIFO. Пример использования (рис. 11):

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int main()

{

  int fd_fifo; /*дескриптор FIFO*/

  char buffer[]="Текстовая строка для fifo\n";

  char buf[100];

   

  /*Если файл с таким именем существует, удалим его*/

  unlink("/tmp/fifo0001.1");

  /*Создаем FIFO*/

  if((mkfifo("/tmp/fifo0001.1", O_RDWR)) == -1)

  {

    fprintf(stderr, "Невозможно создать fifo.........\n");

    exit(0);

  }

  /*Открываем fifo для чтения и записи*/

  if((fd_fifo=open("/tmp/fifo0001.1", O_RDWR)) == - 1)

  {

    fprintf(stderr, "Невозможно открыть fifo.....\n");

    exit(0);

  }

  write(fd_fifo,buffer,strlen(buffer)) ;

  if(read(fd_fifo, &buf, sizeof(buf)) == -1)

  fprintf(stderr, "Невозможно прочесть из FIFO.......\n");

  else

  printf("Прочитано из FIFO : %s\n",buf);

  return 0;

}

Рис. 11. Обмен через объект FIFO.

Если в системе отсутствует функция mkfifo(), можно воспользоваться общей функцией для создания файла

int mknod(char *pathname, int mode, int dev);
Pathname указывает обычное имя каталога Unix и имя FIFO. Режим указывается константой S_IFIFO из заголовочного файла <sys/stat.h>. Здесь же указываются права доступа. Параметр dev не нужен. Пример вызова mknod:

if(mknod("/tmp/fifo0001.1", S_IFIFO | S_IRUSR | S_IWUSR, 0) == - 1)

{ /*Невозможно создать fifo */

Если при открытии FIFO через open не указать режим O_NONBLOCK, открытие FIFO блокируется и для записи, и для чтения. При записи канал блокируется до тех пор, пока другой процесс не откроет FIFO для чтения.При чтении канал снова блокируется до тех пор, пока другой процесс не запишет в FIFO.

Флаг O_NONBLOCK может использоваться только при доступе для чтения. При попытке открыть FIFO с O_NONBLOCK для записи возникает ошибка открытия. Если FIFO закрыть для записи через close или fclose, это значит, что для чтения в FIFO помещается EOF.

Если несколько процессов пишут в один и тот же FIFO, необходимо обратить внимание на то, чтобы сразу не записывалось больше чем PIPE_BUF байтов. Это необходимо, чтобы данные не смешивались друг с другом. Установить пределы записи можно следующей программой (рис. 12):

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

 

int main()

{

  unlink("fifo0001");

  /*Создаем новый FIFO*/

  if((mkfifo("fifo0001", O_RDWR)) == -1)

  {

    fprintf(stderr, "Невозможно создать FIFO\n");

    exit(0);

  }

  printf("Можно записать в FIFO сразу %ld байтов\n",

  pathconf("fifo0001", _PC_PIPE_BUF));

  printf("Одновременно можно открыть %ld FIFO \n", sysconf(_SC_OPEN_MAX));

  return 0;

}

Рис. 12. Ограничение записи в FIFO.

При попытке записи в FIFO, который не открыт в данный момент для чтения ни одним процессом, генерируется сигнал SIGPIPE.

В следующем примере организуется обработчик сигнала SIGPIPE, создается FIFO, процесс-потомок записывает данные в этот FIFO, а родитель читает их оттуда. Пример иллюстрирует простое приложение типа клиент/сервер (рис. 13).

#include <stdio.h>

#include <unistd.h>

#include <sys/types.h>

#include <sys/stat.h>

#include <fcntl.h>

#include <signal.h>

static volatile sig_atomic_t sflag;

static sigset_t signal_new, signal_old, signal_leer;

 

static void sigfunc(int sig_nr)

{

  fprintf(stderr, "SIGPIPE вызывает завершение программы\n");

  exit(0);

}

 

void signal_pipe(void)

{

  if(signal(SIGPIPE, sigfunc) == SIG_ERR)

  {

    fprintf(stderr, "Невозможно получить сигнал SIGPIPE\n");

    exit(0);

  }

  /*Удаляем все сигналы из множества сигналов*/

  sigemptyset(&signal_leer);

  sigemptyset(&signal_new);

  sigaddset(&signal_new, SIGPIPE);

  /*Устанавливаем signal_new и сохраняем его*/

  /* теперь маской сигналов будет signal_old*/

  if(sigprocmask(SIG_UNBLOCK, &signal_new, &signal_old) < 0)

  exit(0);

}

 

int main()

{

  int r_fifo, w_fifo; /*дескрипторы FIFO*/

  char buffer[]="Текстовая строка для fifo\n";

  char buf[100];

  pid_t pid;

  signal_pipe();

  unlink("/tmp/fifo0001.1");

  /*Создаем FIFO*/

  if((mkfifo("/tmp/fifo0001.1", O_RDWR)) == -1)

  {

    fprintf(stderr, "Невозможно создать fifo.........\n");

    exit(0);

  }

  pid=fork();

  if(pid == -1)

    { perror("fork"); exit(0);}

  else if(pid > 0) /*Родитель читает из FIFO*/

  {

    if (( r_fifo=open("/tmp/fifo0001.1", O_RDONLY)) < 0)

      { perror("r_fifo open"); exit(0); }

    while(wait(NULL)!=pid); /*Ждем окончания потомка*/

    read(r_fifo, &buf, sizeof(buf)); /*Читаем из FIFO*/

    printf("%s\n",buf);

    close(r_fifo);

  }

  else /*Потомок записывает в FIFO*/

  {

    if((w_fifo=open("/tmp/fifo0001.1", O_WRONLY)) < 0)

      { perror("w_fifo open"); exit(0); }

    write(w_fifo, buffer, strlen(buffer)); /*Записываем в FIFO*/

    close(w_fifo); /*EOF*/

    exit(0);

  }

  return 0;

}

Рис. 13. Реализация архитектуры клиент/сервер через FIFO.


next up previous contents
Next: Блокировка файлов. Up: Трубы (pipes) Previous: Функция popen().   Contents
2003-12-09