Каналы - общее сведения
Программный канал в Unix/Linux представляет собой одно из средств
взаимодействия между процессами. Само название (pipe, дословно - трубка)
достаточно точно передает смысл функционирования этого средства. Канал
подобен трубопроводу, проложенному между двумя процессами, и по этому
трубопроводу процессы могут пересылать друг другу данные. Подобно
трубопроводу, канал имеет собственную емкость, данные, направленные в канал
процессом-отправителем, не обязательно должны быть немедленно прочитаны
процессом-получателем, но могут накапливаться в канале. Как и у
трубопровода, емкость канала конечна, когда она будет исчерпана, запись в
канал становится невозможной.
Операционные системы Unix/Linux представляеют в распоряжение программистов
два вида каналов - именованные и неименованные. Работа с обоими видами во
многом подобна работе с файлами.
Неименованные каналы
Неименованный канал является средством взаимодействия между связанными
процессами - родительским и дочерним. Родительский процесс создает канал
при помощи системного вызова:
int pipe(int fd[2]);
Массив из двух целых чисел является выходным параметром этого системного
вызова. Если вызов выполнился нормально, то этот массив содержит два
файловых дескриптора. fd[0] является дескриптором для чтения
из канала, fd[1] - дескриптором для записи в канал. Когда
процесс порождает другой процесс, дескрипторы родительского процесса
наследуются дочерним процессом, и, таким образом, прокладывается
трубопровод между двумя процессами. Естественно, что один из процессов
использует канал только для чтения, а другой - только для записи (сами
представьте себе, что произойдет, если это правило будет нарушаться).
Поэтому, если, например, через канал должны передаваться данные из
родительского процесса в дочерний, родительский процесс сразу после
запуска дочернего процесса закрывает дескриптор канала для чтения, а
дочерний процесс закрывает дескриптор для записи. Если нужен
двунаправленный обмен данными между процессами, то родительский процесс
создает два канала, один из которых используется для передачи данных в
одну сторону, а другой - в другую. После получения процессами дескрипторов
канала для работы с каналом используются файловые системные вызовы:
int read(int pipe_fd, void *area, int cnt);
int write(int pipe_fd, void *area, int cnt);
Первый аргумент этих вызовов - дескриптор канала, второй - указатель на
область памяти, с которой происходит обмен, третий - количество байт. Оба
вызова возвращают число переданных байт (или -1 - при ошибке).
Выполнение этих системных вызовов может переводить процесс в состояние
ожидания. Это происходит, если процесс пытается читать данные из пустого
канала или писать данные в переполненный канал. Процесс выходит из
ожидания, когда в канале появляются данные или когда в канале появляется
свободное место, соответственно.
При завершении использования канала процесс выполняет системный вызов:
int close(int pipe_fd);
Если родительский процесс, создавший канал, порождает несколько дочерних
процессов, то все дочерние процессы подключены к другому концу канала.
Если, например, родительский процесс выводит данные в канал, то они
"достанутся" тому дочернему процессу, который раньше выполнит системный
вызов read.
Пример выполнения приведен здесь.
Справочный материал
Избранные системные вызовы Linux/Unix. Краткое описание.
Cправочник библиотечных функция языка С: часть 1,
часть 2 (кодировка кириллица ibm866).