Работа с каналами

Канал - это неименованный объект, позволяющий процессам обмениваться данными.

При этом один из процессов записывает данные в канал, а другой их считывает. Такой тип файла называется также файлом FIFO. В файле FIFO блоки данных образуют очередь, в которой есть указатели чтения и записи (указывающие на голову и на хвост очереди), которые позволяют сохранять порядок элементов очереди. Максимальный размер элемента очереди в байтах задается системной переменной PIPE_BUF, которая определена в файле limits.h.

В оболочке неименованные каналы применяются для создания конвейеров команд. Большинство неименованных каналов создается именно оболочкой. Канал между процессами обозначается символом | (вертикальная черта). В следующем примере на экран выдается вывод команды ls:
ls | pr

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

Функции работы с каналами

Функция pipe создает канал между двумя процессами и возвращает два дескриптора файла. Дескриптор 0 открывается для чтения. Дескриптор 1 открывается для записи. Операция чтения получает данные в порядке их записи. Описанные дескрипторы файлов применяются функциями read, write и close.

В следующем примере дочерний процесс создает канал для связи с родительским процессом и отправляет по нему свой ИД:
#include <sys/types.h>
main()
{
        int p[2];
        char buf[80];
        pid_t pid;

        if (pipe(p))
        {
                     perror("ошибка канала");
                exit(1)'
        }
        if ((pid=fork()) == 0)
        {
                                       /* дочерний процесс */
                close(p[0]);           /*закрывает ненужный*/
                                       *дескриптор чтения*/
                sprintf(buf,"%d",getpid()); 
                                       /* создание данных */
                                       /*для отправки*/
                write(p[1],buf,strlen(buf)+1);
                        /*запись данных с учетом
                        /*байта null*/
                exit(0);
        }
                                        /*родительский процесс*/
        close(p[1]);                    /*закрыть ненужную сторону канала */
        read(p[0],buf,sizeof(buf));     /*чтение данных из канала*/
        printf("Сообщение дочернего процесса: %s/n", buf);
                                       /*вывод результата*/
        exit(0);
}

При попытке чтения из пустого канала операция будет отложена до появления данных в канале. При попытке записи в переполненный канал (PIPE_BUF), операция будет отложена до освобождения необходимого пространства в канале. Если к моменту чтения данных из канала дескриптор записи будет уже закрыт, то операция чтения вернет признак конца файла.

К прочим функциям, предназначенным для работы с каналом, относятся popen и pclose.
popen
Создает канал (с помощью функции pipe) и копию процесса, из которого она была вызвана. Дочерний процесс закрывает ненужный дескриптор канала (в зависимости от того, будет ли он записывать или считывать данные) и вызывает оболочку для запуска требуемого процесса с помощью функции execl.

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

Ниже описан удобный способ связывания дескриптора файла канала со стандартным вводом процесса:

close(p[1]);
close(0);
dup(p[0]);
close(p[0]);

Функция close освобождает дескриптор 0 - стандартный ввод. Функция dup возвращает копию ранее открытого дескриптора файла. Дескрипторы файлов выделяются в порядке возрастания их номеров, причем всегда возвращается первый свободный дескриптор. Следовательно, функция dup выделит для дескриптора чтения канала дескриптор 0. Таким образом, стандартный ввод будет связан с дескриптором чтения канала. Старый дескриптор чтения закрывается. Аналогичное действие должно выполняться дочерним процессом, который записывает данные в канал связи с родительским процессом.

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

Функция pclose дожидается завершения процесса, закрывает канал и возвращает код завершения команды. Эта функция удобнее функции close, так как она позволяет дочернему процессу закончить работу, и только после этого закрывает канал. Кроме того, в процессе может быть только ограниченное число незавершенных дочерних процессов, даже с учетом тех процессов, которые фактически уже выполнили свою задачу. Функция pclose всегда дожидается завершения процесса, поэтому данное ограничение никогда не будет превышено.