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

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

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

С точки зрения ядра ОС FIFO является одним из вариантов реализации трубы. Системный вызов mkfifo() предоставляет процессу именованную трубу в виде объекта файловой системы. Как и для любого другого объекта, необходимо предоставлять процессам права доступа в 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. Пример использования приведен ниже:

#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;

}

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

int mknod(char *pathname, int mode, int dev);

Здесь pathname указывает обычное имя каталога и имя 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 для чтения. При чтении канал снова блокируется до тех пор, пока другой процесс не запишет данные.

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

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

#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;

}

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

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

#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);

     /*Читаем из FIFO*/

    read(r_fifo, &buf, sizeof(buf));

    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); }

    /*Записываем в FIFO*/

    write(w_fifo, buffer, strlen(buffer));

    close(w_fifo); /*EOF*/

    exit(0);

  }

  return 0;

}


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