Именованный канал

Материал из Национальной библиотеки им. Н. Э. Баумана
Последнее изменение этой страницы: 19:35, 20 июля 2016.

Именованный канал или именованный конвейер (англ. named pipe) — один из методов межпроцессного взаимодействия, расширение понятия конвейера в UNIX и подобных ОС. Именованный канал позволяет различным процессам обмениваться данными, даже если программы, выполняющиеся в этих процессах, изначально не были написаны для взаимодействия с другими программами. Это понятие также существует и в Microsoft Windows, хотя там его семантика существенно отличается. Традиционный канал — «безымянен», потому что существует анонимно и только во время выполнения процесса. Именованный канал — существует в системе и после завершения процесса. Он должен быть «отсоединён» или удалён, когда уже не используется. Процессы обычно подсоединяются к каналу для осуществления взаимодействия между ними.

Структура

Для создания именованных каналов будем использовать функцию, mkfifo():

 #include <sys/stat.h>
 int mkfifo(const char *pathname, mode_t mode);

Функция создает специальный FIFO файл с именем pathname, а параметр mode задает права доступа к файлу.

Примечание:
mode используется в сочетании с текущим значением /code>umask</code> следующим образом: (mode & ~umask). Результатом этой операции и будет новое значение umask для создаваемого нами файла. По этой причине мы используем 0777 (S_IRWXO | S_IRWXG | S_IRWXU), чтобы не затирать ни один бит текущей маски.

Как только файл создан, любой процесс может открыть этот файл для чтения или записи также, как открывает обычный файл. Однако, для корректного использования файла, необходимо открыть его одновременно двумя процессами/потоками, одним для получение данных (чтение файла), другим на передачу (запись в файл).

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

Типичные ошибки, которые могут возникнуть во время создания канала:

  • EACCES — нет прав на запуск (execute) в одной из директорий в пути pathname
  • EEXIST — файл pathname уже существует, даже если файл — символическая ссылка
  • ENOENT — не существует какой-либо директории, упомянутой в pathname, либо является битой ссылкой
  • ENOSPC — нет места для создания нового файла
  • ENOTDIR — одна из директорий, упомянутых в pathname, на самом деле не является таковой
  • EROFS — попытка создать FIFO файл на файловой системе «только-на-чтение»

Чтение и запись в созданный файл производится с помощью функций read() и write().

Пример:

 #include <sys/stat.h>
 #include <fcntl.h>
 #include <string.h>
 #include <stdio.h>
 #define NAMEDPIPE_NAME "/tmp/my_named_pipe"
 #define BUFSIZE        50
 int main (int argc, char ** argv) {
    int fd, len;
    char buf[BUFSIZE];
    if ( mkfifo(NAMEDPIPE_NAME, 0777) ) {
        perror("mkfifo");
        return 1;
    }
    printf("%s is created\n", NAMEDPIPE_NAME);
    if ( (fd = open(NAMEDPIPE_NAME, O_RDONLY)) <= 0 ) {
        perror("open");
        return 1;
    }
    printf("%s is opened\n", NAMEDPIPE_NAME);
    do {
        memset(buf, '\0', BUFSIZE);
        if ( (len = read(fd, buf, BUFSIZE-1)) <= 0 ) {
            perror("read");
            close(fd);
            remove(NAMEDPIPE_NAME);
            return 0;
        }
        printf("Incomming message (%d): %s\n", len, buf);
    } while ( 1 );
}

Мы открываем файл только для чтения (O_RDONLY). И могли бы использовать O_NONBLOCK модификатор, предназначенный специально для FIFO файлов, чтобы не ждать когда с другой стороны файл откроют для записи. Но в приведенном коде такой способ неудобен.

Компилируем программу, затем запускаем ее:

$ gcc -o mkfifo mkfifo.c
$ ./mkfifo

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

$ echo 'Hello, my named pipe!' > /tmp/my_named_pipe

В результате мы увидим следующий вывод от программы:

$ ./mkfifo 
/tmp/my_named_pipe is created
/tmp/my_named_pipe is opened
Incomming message (22): Hello, my named pipe!
read: Success

Именованные каналы в UNIX

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

Так же существуют именованные каналы, которые так же иногда называют FIFO, что означает “First In, First Out”. Тебе, как программисту, это должно быть понятно: первым вошёл, первым вышел. Этот принцип относится абсолютно ко всем байтам данным, попадающим в именованный канал. Каналы хранятся как файлы в файловой системе. Таким образом, “имя” такого канала – имя файла. И это вполне логично, т.к. всем известно, что в ОС UNIX любой элемент может быть отображен в виде файла, включая устройства и наши именованные каналы.

Таким образом, посмотреть список каналов можно командой ls. Ранее для создания именованных каналов использовалась утилита mknod. Сейчас же стандартом является mkfifo. Синтаксис использования достаточно прост. Нужно лишь передать программе имя, которое будет носить именованный канал.

FIFO2.jpg

Буква “p”, что подчеркнута красным, как раз и означает, что тип этого файла – именованный канал.

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

FIFO3.jpg

Это – самый простой пример работы именованного канала. Откроем первую консоль и напишем в ней:

 ls -l > pipe 

Теперь открываем вторую консоль и переходим в эту же директорию. Выполняем:

 cat < pipe 

Вуаля! И мы видим список файлов и поддиректорий нашей папки. (см. рис. 3) Интересной особенностью является то, что порядок выполнения команд не имеет значения. Т.е. можно сначала выполнить чтение из канала, а только потом подать команду записи в него. Результат будет один. После приёма первой команды будет выполняться “ожидание” второй команды.

Такой способ очень прост и эффективен для взаимодействия двух разных процессов. Допустим, что у нас существует сервер, который ожидает приёма данных (например, данные для печати), открыв канал для чтения. Клиенту лишь нужно будет подать команду на запись данных в пайп. Печать начнётся сразу же по окончанию записи данных клиентом.

Именованный канал в Windows

В Windows дизайн именованных каналов смещён к взаимодействию «клиент-сервер», и они работают во многом как сокеты: помимо обычных операций чтения и записи, именованные каналы в Windows поддерживают явный «пассивный» режим для серверных приложений (для сравнения: сокет домена UNIX). Windows 95 поддерживает клиенты именованных каналов, а системы ветви Windows NT могут служить также и серверами.

К именованному каналу можно обращаться в значительной степени как к файлу. Можно использовать функции Windows API CreateFile, CloseHandle, ReadFile, WriteFile, чтобы открывать и закрывать канал, выполнять чтение и запись. Функции стандартной библиотеки Си, такие, как fopen, fread, fwrite и fclose, тоже можно использовать, в отличие от сокетов Windows (англ.), которые не реализуют использование стандартных файловых операций в сети. Интерфейс командной строки (как в Unix) отсутствует.

Именованные каналы — не существуют постоянно и не могут, в отличие от Unix, быть созданы как специальные файлы в произвольной доступной для записи файловой системе, но имеют временные имена (освобождаемые после закрытия последней ссылки на них), которые выделяются в корне файловой системы именованных каналов (англ. named pipe filesystem, NPFS) и монтируются по специальному пути «\\.\pipe\» (то есть у канала под названием «foo» полное имя будет «\\.\pipe\foo»). Анонимные каналы, использующиеся в конвейерах, — это на самом деле именованные каналы со случайным именем.

Именованные каналы обычно не доступны непосредственно пользователю, но есть существенные исключения. Например, средство виртуализации рабочих станций VMWare может открывать эмулируемый последовательный порт для главной системы как именованный канал, а отладчик уровня ядра kd от Microsoft поддерживает именованные каналы в качестве средства сообщения при отладке (фактически, так как kd обычно требует подключения к целевому компьютеру по последовательному порту, VMware и kd можно соединить вместе для отладки драйверов устройств на одном компьютере). Обе программы требуют от пользователя указания имён канала в виде «\\.\pipe\имя».

Именованные каналы в Windows NT могут наследовать контекст безопасности.

Именованные каналы в сетях Windows

Именованные каналы — это также сетевой протокол в SMB, основанный на использовании особой части межпроцессного взаимодействия (IPC). IPC в SMB может бесшовно и прозрачно передавать контекст аутентификации пользователя на другую сторону именованного канала. Наследование аутентификации для именованных каналов Windows NT для пользователя и разработчика настолько прозрачно, что почти незаметно, в связи с чем его часто неправильно понимают.

Ссылки