Каналы часто используются для передачи данных программе, выполняющейся как подпроцесс (или приема данных от нее). Специально для этих целей предназначены функции
popen
и
pclose
, устраняющие необходимость в вызове функций
pipe
,
dup2
,
exec
и
fdopen
.
Сравните листинг 5.9 с предыдущим примером (листинг 5.8).
Листинг 5.9. (popen.c) Использование функций
popen
и
pclose
#include <stdio.h>
#include <unistd.h>
int main {
FILE* stream = popen("sort", "w");
fprintf(stream, "This is a test.\n");
fprintf(stream, "Hello, world.\n");
fprintf(stream, "My dog has fleas\n");
fprintf(stream, "This program is great.\n");
fprintf(stream, "One fish, two fish.\n");
return pclose(stream);
}
Функция
popen
создает дочерний процесс, в котором выполняется команда
sort
. Один этот вызов заменяет вызовы функций
pipe
,
fork
,
dup2
и
execlp
. Второй аргумент,
"w"
, указывает на то, что текущий процесс хочет осуществлять запись в дочерний процесс. Функция
popen
возвращает указатель на один из концов канала; второй конец соединяется со стандартным входным потоком дочернего процесса. Функция
pclose
закрывает входной поток дочернего процесса, дожидается его завершения и возвращает код статуса.
Первый аргумент функции
popen
является командой интерпретатора, выполняемой в подпроцессе
/bin/sh
. Интерпретатор просматривает переменную среды
PATH
, чтобы определить, где следует искать команду. Если второй аргумент равен
"r"
, функция возвращает указатель на стандартный выходной поток дочернего процесса, чтобы программа могла читать данные из него. Если второй аргумент равен
"w"
, функция возвращает указатель на стандартный входной поток дочернего процесса, чтобы программа могла записывать данные в него. В случае ошибки возвращается пустой указатель.
Функция
pclose
закрывает поток, указатель на который был возвращен функцией
popen
, и дожидается завершения дочернего процесса.
5.4.5. Каналы FIFO
Файл FIFO (First-In, First-Out — первым пришел, первым обслужен) — это канал, у которого есть имя в файловой системе. Любой процесс может открыть и закрыть такой файл. Процессы, находящиеся на противоположных концах канала, не обязаны быть связанными друг с другом. FIFO-файлы называют именованными каналами.
FIFO-файл создается с помощью команды
mkfifo
. Путь к файлу указывается в командной строке, например:
% mkfifo /tmp/fifo
% ls -l /tmp/fifo
prw-rw-rw- 1 samuel users 0 Jan 16 14:04 /tmp/fifo
Первый символ в строке режима (
p
) указывает на то, что файл имеет тип FIFO (именованный канал). Теперь в одном терминальном окне можно осуществлять чтение из файла с помощью команды
% cat < /tmp/fifo
а
в другом окне можно выполнять запись в файл:
% cat > /tmp/fifo
Попробуйте во втором окне ввести какой-то текст и нажать <Enter>. Введенный текст немедленно отобразится в первом окне. Канал закрывается нажатием клавиш <Ctrl+D> во втором окне. FIFO-файл удаляется с помощью следующей команды:
% rm /tmp/fifo
Создание FIFO-файла
FIFO-файл можно создать программным путем с помощью функции
mkfifo
. Первым аргументом является путь к файлу. Второй аргумент задает права доступа к каналу со стороны его владельца, группы и остальных пользователей (об этом пойдет речь в разделе 10.3, "Права доступа к файлам"). Поскольку у канала есть читающая и записывающая стороны, права доступа должны учитывать оба случая. Если канал не может быть создан (например, файл с таким именем уже существует), функция
mkfifo
возвращает -1. Для работы функции требуется подключить к программе файлы
<sys/types.h>
и
<sys/stat.h>
.
Доступ к FIFO-файлу
К FIFO-файлу можно обращаться как к обычному файлу. При организации межзадачного взаимодействия одна программа должна открыть файл для записи, а другая - для чтения. Над файлом можно выполнять как низкоуровневые (
open
,
write
,
read
,
close
и др.), так и высокоуровневые (
fopen
,
fprintf
,
fscanf
,
fclose
и др.) функции.
Например, на низком уровне запись блока данных в FIFO-файл осуществляется следующим образом:
int fd = open(fifo_path, O_WRONLY);
write(fd, data, data_length);
close(fd);
А так выполняется чтение строки из FIFO-файла на высоком уровне:
FILE* fifo = fopen(fifo_path, "r");
fscanf(fifo, "%s", buffer);
fclose(fifo);
У FIFO-файла одновременно может быть несколько читающих и записывающих программ. Входные потоки разбиваются на атомарные блоки, размер которых определяется константой
PIPE_BUF
(4 Кбайт в Linux). Если несколько программ параллельно друг другу осуществляют запись в файл, их блоки будут чередоваться. То же самое относится к программам. одновременно читающим данные из файла.
Отличия от именованных каналов в Windows
Каналы операционных систем семейства Win32 очень напоминают каналы Linux. Основное различие касается именованных каналов, которые в Win32 функционируют скорее как сокеты. Именованные каналы Win32 способны соединять по сети процессы, выполняющиеся на разных компьютерах. В Linux для этой цели используются именно сокеты. Кроме того, в Win32 допускается, чтобы несколько программ чтения или записи работали с именованным каналом, не перекрывая потоки друг друга, а сами каналы поддерживают двунаправленный обмен данными. [17]
17
Именованный канал можно создать только в Windows NT. В Windows 9х программы могут устанавливать только клиентские соединения.