Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Учеб.пос.СП.doc
Скачиваний:
28
Добавлен:
31.03.2015
Размер:
1.33 Mб
Скачать

5.3. Двунаправленное взаимодействие с использованием однонаправленных каналов

Типичная командная оболочка не позволяет организовать двунаправленное взаимодействие процессов. Двунаправленные конвейеры создаются в программах, написанных на языке С. Начнем с простого примера. Допустим, мы хотим вызвать в программе команду sort для сортировки данных. Конечно, это можно было бы сделать подобным образом:

system(“sort < файл_с_данными > файл_вывода”);

После этого можно прочитать отсортированные данные из файла_вывода. Нам нужно, чтобы команда sort получила данные по каналу и отправила нам отсортированный вывод тоже по каналу. Команда sort может читать свой стандартный источник ввода и записывать данные в стандартный вывод. Нам уже известно, как сделать произвольный дескриптор файла стандартным вводом или выводом процесса. В следующем примере воспользуемся двумя каналами, каждый из которых будет обрабатывать однонаправленный трафик. Один канал будет обрабатывать данные, поступающие к sort, а второй – данные, передаваемые обратно:

void fsort(void)

{

int pfdout[2], pfdin[2], fd;

ssize_t nread;

pid_t pid;

char buf[512];

pipe(pfdout);

pipe(pfdin);

pid = fork();

if (pid ==0) { /* дочерний процесс */

dup2(pfdout[0], STDIN_FILENO);

close(pfdout[0]);

close(pfdout[1]);

dup2(pfdin[1], STDOUT_FILENO);

close(pfdin[0]);

close(pfdin[1]);

execlp(“sort”, “sort”, NULL);

EC_FAIL

}

/* родительский процесс */

close(pfdout[0]);

close(pfdin[1]);

fd = open(“datafile”, O_RDONLY);

while (true) {

nread = read(fd, buf, sizeof(buf));

if (nread == 0)

break;

write(pfdout[1], buf, nread);

}

close(fd);

close(pfdout[1]);

while (true) {

nread = read(pfdin[0], buf, sizeof(buf));

if (nread == 0)

break;

write(STDOUT_FILENO, buf, nread);

}

close(pfdin[0]);

waitpid(pid, NULL, 0);

return;

EC_CLEANUP_BGN

EC_FLUSH(“fsort”);

EC_CLEANUP_END

}

Содержимое файла datafile таково:

mango

tomato

banana

apple

В результате выполнения программы получим следующее:

apple

banana

mango

tomato

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

Контрольные вопросы

  1. Можно ли реализовать оператор go to как внешнюю команду, чтобы она выполнялась как дочерний процесс?

  2. Напишите программу, которая записывает свой идентификатор процесса в стандартный вывод, после чего читает список идентификаторов процессов из своего стандартного источника ввода. Если в списке есть ее собственный идентификатор, она выводит список, в противном случае добавляет в список свой идентификатор процесса и записывает весь список в свой стандартный вывод. Затем все повторяется. Создайте пять процессов, выполняющих эту программу, организуйте их в кольцо и позвольте им немного поиграть.

  3. Напишите клиентскую программу для калькулятора, позволяющую пользователю вводить выражение с использованием инфиксной нотации (2+3).

  4. Напишите клиентскую программу для калькулятора, позволяющую пользователю вводить выражение с использованием постфиксной нотации (2 3+).

  5. Измените, последний пример из раздела 5.2 так, чтобы процесс wc был родителем who.