Именованные каналы (FIFOs - First In First Out)

Пост на тему Именованные каналы (FIFOs - First In First Out)

Введение

Система Linux IPC (Inter-process communication) предоставляет средства для взаимодействия процессов между собой.

В распоряжении программистов есть несколько методов IPC:

  • полудуплексные каналы UNIX
  • FIFO (именованные каналы)
  • Очереди сообщений в стиле SYSV
  • Множества семафоров в стиле SYSV
  • Разделяемые сегменты памяти в стиле SYSV
  • Сетевые сокеты (в стиле Berkeley) (не охватывается этой статьей)
  • Полнодуплексные каналы (каналы потоков) (не охватывается этой статьей)

Если эти возможности эффективно используются, то они обеспечивают солидную базу для поддержания идеологии клиент/сервер в любой UNIX-системе, включая Linux.

Основные понятия

Именованные каналы во многом работают так же, как и обычные каналы, но все же имеют несколько заметных отличий.

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

Создание FIFO

Есть несколько способов создания именованного канала. Первые два могут быть осуществлены непосредственно из shell-а.


mknod MYFIFO p
mkfifo a=rw MYFIFO

Эти две команды выполняют идентичные операции, за одним исключением. Команда mkfifo предоставляет возможность для изменения прав доступа к файлу FIFO непосредственно после создания. При использовании mknod будет необходим вызов команды chmod.

Файлы FIFO могут быть быстро идентифицированы в физической файловой системе посредством индикатора “p”, представленного здесь в длинном листинге директории.


$ ls -1 MYFIFO
^prw-r--r--  1 root  root      0 Dec 14 22:15 MYFIFO| ...

Также заметьте, что вертикальный разделитель располагается непосредственно после имени файла. Другая веская причина запустить


Linux, eh?

Чтобы создать FIFO на Си, мы можем прибегнуть к использованию системного вызова mknod():


LIBRARY FUNCTION: mknod();
PROTOTYPE: int mknod( char *pathname, mode_t mode, dev_t dev );
   RETURNS: 0 в случае успеха,
            -1 в случае ошибки:
               errno = EFAULT (ошибочно указан путь)
                       EACCESS (нет прав)
                       ENAMETOOLONG (слишком длинный путь)
                       ENOENT (ошибочно указан путь)
                       ENOTDIR (ошибочно указан путь)
                       (остальные смотрите в man page для mknod)
     NOTES: Создает узел файловой системы (файл, файл устройства или
FIFO)

Оставим более детальное обсуждение mknod()-а man page, а сейчас давайте рассмотрим простой пример создания FIFO на Си:


   mknod("/tmp/MYFIFO", S_IFIFO|0666, 0);

В данном случае файл “/tmp/MYFIFO” создан как FIFO-файл. Требуемые права - это “0666”, хотя они находятся под влиянием установки umask, как например:


   final_umask = requested_permissions & ~original_umask ...

Общая хитрость - использовать системный вызов umask() для того, чтобы временно устранить значение umask-а:


   umask(0);
   mknod("/tmp/MYFIFO", S_IFIFO|0666, 0);

Кроме того, третий аргумент mknod()-а игнорируется, в противном случае мы создаем файл устройства. В этом случае он должен отметить верхнее и нижнее числа файла устройства.

Операции FIFO

Операции ввода/вывода FIFO, по существу, такие же, как для обычных каналов, за одним исключением. Чтобы физически открыть проход к каналу, должен быть использован системный вызов “open” или библиотечная функция. С полудуплексными каналами это невозможно, поскольку канал находится в ядре, а не в физической файловой системе. В нашем примере мы будем трактовать канал как поток, открывая его fopen()-ом и закрывая fclose()-ом.

Рассмотрим простой сервер-процесс:


/****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ****************************************************************************
 MODULE: fifoserver.c
 ****************************************************************************
#include
#include #include
#include
#include
#define FIFO_FILE   "MYFIFO"
int main(void)
{
   FILE *fp;
   char readbuf[80];
   /* Создаем FIFO, если он еще не существует */
   umask(0);
   mknod(FIFO_FILE, S_IFIFO|0666, 0);
   while(1)
   {
      fp = fopen(FIFO_FILE, "r");
      fgets(readbuf, 80, fp);
      printf("Received string: %s\n", readbuf);
      fclose(fp);
   }
   return(0);
}

Поскольку FIFO блокирует по умолчанию, запустим сервер фоном после того, как его откомпилировали:


   $ fifoserver&

Скоро мы обсудим действие блокирования, но сначала рассмотрим следующего простого клиента для нашего сервера:


/****************************************************************************
 Excerpt from "Linux Programmer's Guide - Chapter 6"
 (C)opyright 1994-1995, Scott Burkett
 ****************************************************************************
 MODULE: fifoclient.c
 ****************************************************************************
#include
#include
#define FIFO_FILE   "MYFIFO"int main(int argc, char *argv[])
{
   FILE *fp;
   if ( argc != 2 ) {
      printf("USAGE: fifoclient [string]\n");
      exit(1);
   }
   fputs(argv[1], fp);
   fclose(fp);
   return(0);
}

Действие блокирования над FIFO

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

В примере с нашим простым сервером мы только засунули его в фон и позволили там осуществлять блокирование. Альтернативой могло бы быть перепрыгивание на другую виртуальную консоль и запуск клиента, переключение туда и обратно, чтобы увидеть результат.

Демидова Е. А.
Демидова Е. А.
Студентка

. В сферу моих интересов входят компьютерные науки, нейронные сети и высшая математика