Чтение онлайн

ЖАНРЫ

Linux: Полное руководство

Аллен Питер В.

Шрифт:

Вызов system порождает дочерний процесс, позволяя ему читать данные со стандартного ввода (stdin) и писать на стандартный вывод (stdout). Иногда нам нужно передать данные дочернему процессу или, наоборот, получить информацию от порожденного процесса. Другими словами, мы хотим, чтобы дочерний процесс получал данные не со стандартного ввода, а от родительского процесса или/и выводил информацию не на стандартный вывод, а передавал ее процессу-предку. Ввод/вывод между процессами осуществляется с помощью системного вызова popen. Этот вызов должен быть выполнен ДО вызова fork, чтобы файловые дескрипторы были унаследованы дочерним процессом.

FILE * popen(const char *
команда, const char * режим_доступа);

Первый параметр — это название программы, которую мы хотим запустить в дочернем процессе. Второй параметр определяет режим доступа. Установите значение «r», если вам нужно читать вывод дочернего процесса, если же вам нужно передать информацию на стандартный ввод порожденного процесса, установите значение «w». Режима двустороннего обмена не существует.

Вызов popen возвращает указатель FILE* или пустой указатель NULL, если вызов не удался. Так же, как и при работе с обыкновенными файлами, после завершения операции ввода/вывода вы должны закрыть канал вызовом pclose. Во время работы с потоком рекомендую использовать вызов fflush, чтобы предотвратить задержки из-за буферизации.

Теперь несколько простых примеров. Предположим, что нам нужно вывести на стандартный вывод имена всех текстовых файлов, содержащихся в текущем каталоге. Это можно очень просто сделать с помощью вызова system:

system("ls *.txt");

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

// открываем поток

FILE *fp = popen("ls *.txt", "r");

// в цикле читаем имена всех текстовых файлов

while ((fname = fgets(..., fp);) != EOF) {

 // обрабатываем полученное значение переменной fname

}

// закрываем поток

pclose(fp);

Этот фрагмент кода в особых комментариях не нуждается. Сначала мы создаем поток для чтения (доступ «r») информации от порожденного процесса (ls *.txt). Затем в цикле while читаем имена файлов до тех пор, пока не будет достигнут конец файла. После окончания операции ввода/вывода закрываем поток вызовом pclose(fp).

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

Листинг 26.1. Родительский процесс

#include <stdio.h>

#include <sys/wait.h>

#include <unistd.h>

int main {

 char buff[1024]={0};

 FILE * cp; // cp - child process - дочерний процесс

 int status;

 // Открываем канал. Дочерний процесс - /usr/bin/child

 cp=popen("/usr/bin/child", "w");

 if (!cp) {

printf("He могу открыть канал.\n");

exit(1);

 }

 printf("Введите
информацию для передачи дочернему процессу ");

 // читаем ввод пользователя

 fgets(buff, sizeof(buff), stdin);

 // передаем данные дочернему процессу

 fprintf(cp, "%s\n", buff);

 // "выталкиваем" содержимое буфера в канал

 fflush(cp);

 // закрываем канал и проверяем состояние вызова pclose

 status=pclose(cp);

 if (!WIFEXITED(status))

printf("ошибка при закрытии канала\n");

 printf("Завершение работы родительского процесса\n");

 return 0;

}

Листинг 26.2. Дочерний процесс — исходный код /usr/bin/child

#include <stdio.h>

int main {

 char buff[1024]={0};

 fgets(buff, sizeof(buff), stdin);

 printf("Прочитал со стандартного ввода: %s\n", buff);

 printf("Завершение работы дочернего процесса\n");

return 0;

}

26.3. Каналы типа FIFO

Канал FIFO — это канал, основанный на принципе очереди: «первым вошел, первым вышел». От обычного канала канал FIFO отличается следующим:

♦ Канал FIFO сохраняется в файловой системе в виде файла, поэтому каналы FIFO называются именованными.

♦ С именованным каналом, как с обычным файлом, могут работать все процессы, а не только предок и потомки.

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

Создать именованный канал можно с помощью командного интерпретатора:

$ mknod myFIFO p

$ mkfifo a=rw myFIFO

или системного вызова mknod:

int mknod(char *pathname, mode_t mode, dev_t dev);

Функция mknod используется не только для создания каналов FIFO. Она может создать любой i-узел (inode) файловой системы: файл, устройство, канал FIFO. Функция возвращает 0, если создание узла прошло успешно, или -1, если произошла ошибка. Проанализировать ошибку можно с помощью переменной

errno
, которая равна:

♦ EFAULT, ENOTDIR, ENOENT — неправильно задан путь;

♦ EACCESS — у вас недостаточно прав;

♦ ENAMETOOLONG — слишком длинный путь.

Пример создания FIFO-канала:

mknod("FIFO", S_IFIFO|0666, 0);

В текущем каталоге будет создан канал FIFO с правами доступа 0666.

Указывая права доступа создаваемого файла, помните, что они находятся под влиянием umask. Поэтому, если вы хотите установить истинное значение прав доступа, используйте системный вызов umask(0), чтобы временно отключить влияние umask:

Поделиться с друзьями: