Next Previous Contents

10. Каналы и FIFO

Канал - механизм для связи между процессами; данные, записывающиеся в канал одним процессом могут читаться другим процессом. Данные обрабатываются в порядке 'первым пришел' - 'первым ушел' (FIFO). Канал не имеет никакого имени; он создан для одного использования, и оба конца должны быть унаследованы от одиночного процесса, который создал канал.

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

Канал или FIFO должен быть открыт с обоих концов одновременно. Если Вы читаете из канала или файла FIFO, в который никто ничего не пишет (возможно потому что, они все закрыли файл, или вышли), то чтение возвращает конец файла. Запись в канал или FIFO, который не имеет процесс считывания, обрабатывается как условие ошибки; это генерирует сигнал SIGPIPE, и сбои с кодом ошибки EPIPE, если сигнал обработан или блокируется.

Ни каналы ни FIFO специальные файлы не позволяют позиционирование файла. И чтение и запись происходит последовательно; чтение из начала файла и запись в конец.

10.1 Создание Канала

Примитив для создания канала - функция pipe. Она создает оба, и чтения и записи концы канала. Это не очень полезно для одиночного процесса, использовать канал, чтобы разговаривать с собой. В типичном использовании, процесс создает канал только прежде, чем он ветвится на один или более дочерних процессов (см. Раздел 23.4 [Создание Процесса]). Канал используется для связи или между родителем или дочерними процессами, или между двумя процессами братьями.

Функция pipe объявлена в заглавном файле 'unistd.h'.

       int pipe (int filedes[2])  (функция)
Функция pipe создает канал и помещает дескрипторы файла для чтения и записи (соответственно) в filedes [0] и filedes [1].

При успехе pipe возвращает значение 0. При отказе, -1. Следующие errno условия ошибки определены для этой функции:

EMFILE

процесс имеет слишком много файлов открытыми.

ENFILE

имеются слишком много открытых файлов во всей системе. См. Раздел 2.2 [Коды Ошибки], для получения более подробной информации о ENFILE.

Вот пример простой программы, которая создает канал. Эта программа использует функцию ветвления (см. Раздел 23.4 [Создание Процесса]) чтобы создать дочерний процесс. Родительский процесс напишет данные, которые читается дочерним процессом.
                #include <sys/types.h>
                #include <unistd.h>
                #include <stdio.h>
                #include <stdlib.h>
                void
                read_from_pipe (int file)
                {
                        FILE *stream;
                        int c;
                        stream = fdopen (file, 'r');
                        while ((c = fgetc (stream)) != EOF)
                                putchar (c);
                        fclose (stream);
                }
        /* Пишем некоторый произвольный текст в канал. */
                void
                write_to_pipe (int file)
                {
                        FILE *stream;
                        stream = fdopen (file, 'w');
                        fprintf (stream, 'hello, world!\n');
                        fprintf (stream, 'goodbye, world!\n');
                        fclose (stream);
                }
                int
                main (void)
                {
                        pid_t pid;
                        int mypipe[2];
                        /* Create the pipe. */
                        if (pipe (mypipe))
                                {
                                        fprintf(stderr,'Pipe failed.\n');
                                        return EXIT_FAILURE;
                                }
                        /* Создаем дочерний процесс. */
                        pid = fork ();
                        if (pid == (pid_t) 0)
                                {
                                        /* Это - дочерний процесс. */
                                        read_from_pipe (mypipe[0]);
                                        return EXIT_SUCCESS;
                                }
                        else if (pid < (pid_t) 0)
                                {
                                        /* The fork failed. */
                                        fprintf(stderr,'Fork failed.\n');
                                        return EXIT_FAILURE;
                                }
                        else
                                {
                                        /* Это - родительский процесс. */
                                        write_to_pipe (mypipe[1]);
                                        return EXIT_SUCCESS;
                                }
                }

10.2 Канал к Подпроцессу

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

Один из способов выполнения этого - использовать комбинацию pipe (чтобы создать канал), fork (чтобы создать подпроцесс), dup2 (чтобы вынудить подпроцесс использовать pipe как стандартный ввод или канал вывода), и exec (чтобы выполнить новую программу). Или, Вы можете использовать popen и pclose.

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

       FILE * popen (const char *command, const char *mode)  (функция)
Popen функция близко связана с функцией системы; см. Раздел 23.1 [Выполнение Команд]. Она выполняет команду оболочки как подпроцесс.

Однако, вместо того, чтобы ждать завершения команды, она создает канал к подпроцессу и возвращает поток, который соответствует этому каналу.

Если Вы определяете аргумент режима 'r', Вы можете читать из потока, чтобы отыскать данные из канала стандартного вывода подпроцесса. Подпроцесс наследует канал стандартного ввода из родительского процесса.

Аналогично, если Вы определяете аргумент режима 'w', Вы можете писать в поток, чтобы посылать данные на канал стандартного ввода подпроцесса. Подпроцесс наследует канал стандартного вывода из родительского процесса.

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

       int pclose (FILE *stream)  (функция)
Pclose функция используется, чтобы закрыть поток, созданный popen. Она ждет завершения дочернего процесса, и возвращает значение состояния, что касается функции системы.

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

                #include <stdio.h>
                #include <stdlib.h>
                void
                write_data (FILE * stream)
                {
                        int i;
                        for (i = 0; i < 100; i++)
                                fprintf (stream, '%d\n', i);
                        if (ferror (stream))
                                {
                                        fprintf (stderr, 'Output to
                                        stream failed.\n');
                                        exit (EXIT_FAILURE);
                                }
                }
                int
                main (void)
                {
                        FILE *output;
                        output = popen ('more', 'w');
                        if (!output)
                                {
                                        fprintf(stderr,'Could not run
                                                        more.\n');
                                        return EXIT_FAILURE;
                                }
                        write_data (output);
                        pclose (output);
                        return EXIT_SUCCESS;
                }

10.3 FIFO Специальные Файлы

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

Если Вы создали FIFO специальный файл таким образом,, любой процесс может открывать его для чтения или записи, таким же образом как обычный файл. Однако, он должен быть открыт в оба конца одновременно прежде, чем Вы можете делать любой ввод или вывод на нем. Открытие FIFO для чтения обычно блокируется, пока некоторый другой процесс не открывает тот же самый FIFO для записи, и наоборот.

Mkfifo функция объявлена в заглавном файле 'sys/stat.h'.

       int mkfifo (const char *filename, mode_t mode)  (функция)
Mkfifo функция делает FIFO специальный файл с именем filename. Аргумент mode используется, чтобы установить права файла; см. Раздел 9.8.7 [Установка Прав].

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

EEXIST

именованный файл уже существует.

ENOSPC

каталог или файловая система не может быть расширен.

EROFS

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

10.4 Быстрота ввода-вывода Канала

Чтение или запись данных в канал мгновенны, если размер данных меньше чем PIPE_BUF. Это означает что передача данных кажется мгновенной, в этом случае ничто в системе не может наблюдать состояние, в котором он является частично полным. Быстрый ввод - вывод не может начинаться сразу же (может требоваться ждать пространство буфера или для данных), но если только он начинается, то он заканчивается немедленно.

Чтение или запись большего количества данных может не быть быстрым; например, выходные данные из других процессов, совместно использующих дескриптор могут быть разбиты на части.

См. Раздел 27.6 [Ограничения для Файлов], для уточнения информации относительно параметра PIPE_BUF.


Next Previous Contents