Задача №2 - неименованные программные каналы

Управление процессами в ОС UNIX

 

Цель работы

Ознакомиться со средствами организации взаимодействия процессов в ОС UNIX.

 

Содержание работы

  1. Ознакомиться со средствами организации взаимодействия процессов в ОС UNIX.
  2. Для указанного варианта составить следующие программы на языке Си:
    • реализующую конвейер из двух фильтров (формулировка одного фильтра задана, второй фильтр необходимо придумать);
    • организующую взаимодействие двух процессов через неименованныйпрограммный канал;

· организующую взаимодействие двух процессов через именованныйпрограммный канал.

Примечание. Проиллюстрировать работу неименованногопрограммного канала и именованногопрограммного канала на макетных программах с применением фильтров, используемых в задаче №1.

3. Защитить лабораторную работу, ответив на контрольные вопросы.

Методические указания к лабораторной работе

 

Таблица 1. Основные системные вызовы, используемые в задачах

  fork() Системный вызов, создающий новый процесс (потомок), который является практически полной копией процесса-родителя, выполняющего этот вызов
  wait(int *status) Ожидание завершения процесса-потомка родительским процессом
  execl() Все формы системного вызова exec превращают вызвавший процесс в новый процесс, который строится из обычного выполняемого файла, называемого в дальнейшем новым выполняемым файлом. Если системный вызов exec закончился успешно, то он не может вернуть управление, так как вызвавший процесс уже заменен новым процессом.
  pipe(p) p – целый массив из двух элементов: int p[2]; Дескриптор файла p[0] используется для представления конца канала, предназначенного для чтения, а дескриптор файла p[1] – для представления конца канала, предназначенного для записи.
  fdopen(<дескриптор_файла> <режим_открытия_файла>) fdopen ассоциирует поток с файловым дескриптором полученным от функции open()
  dup(p[0]) Наименьший доступный дескриптор файла, т.е. 0, и p[0], т.е. конец канала p для чтения, становятся синонимами
  close(0) Дескриптор файла 0 закрывается в связи с закрытием ассоциированного с ним файла, т.е. файла стандартного ввода

 

 

Задача №1 - конвейер из двух фильтров

 

Пример: Программная реализация конвейера ls | wc, подсчитывающего количество программ в текущем каталоге.

void main(void) /* LSWS.C */

/* Создание программного канала */

/* для команд ls и wc */

{

int pid, pid2;

int fd[2];

int status, dead;

switch(pid = fork())

{

case -1: /* Cбой при вызове fork() */

printf("Ошибка при вызове fork() #1 \n");

exit(1);

case 0: /* ПОТОМОК #1 */

pipe(fd);

 

switch(pid2 = fork())

{

case -1: /* Cбой при вызове fork() */

printf("Ошибка при вызове fork() #2 \n");

exit(2);

case 0: /* ПОТОМОК #2 */

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

execl("/usr/bin/wc", "wc", 0);

puts("Ошибка при вызове WC \n");

exit();

default: /* */

close(1); dup(fd[1]);

close(fd[1]); close(fd[0]);

execl("/bin/ls", "ls", 0);

puts("Ошибка при вызове LS\n");

exit();

default: /* ПРЕДОК ГЛАВНЫЙ */

dead = wait(&status);

exit();

}

}

 

 

Задача №2 - неименованные программные каналы

 

Пример: взаимодействие родственных процессов в дуплексном режиме (используются два программных канала) и переназначение стандартного ввода-вывода.

 

Пусть входной файл содержит команды форматирования (они начинаются с ‘.’ в первом символе строки) и текст:

.R

aaaaaaa

.B

ccccccc

. . .

Для решения задачи используем 2 параллельных процесса:

· textcount – читает текстовый файл и при передаче строк процессу count удаляет из них команды форматирования;

· count – подсчитывает получаемые от процесса textcount символы и по достижении конца текста посылает общее число знаков первому процессу.

Порождающий и порожденный процессы должны взаимодействовать, используя два канала p и q.

 

Рис. 1. Взаимодействие родственных процессов в дуплексном режиме

Порожденный процесс выполняет независимо написанную программу (Count), которая читает данные из входного потока stdin (точка 1) и записывает их в стандартный выходной поток stdout (точка 2), т.е. использует функции getchar() и printf().

 

 

#include <stdio.h>

#define R 0 /* stdin */

#define W 1 /* stdout */

#define True 1

#define FALSE 0

#define PERIOD '.'

void main(void) /* Textcount.c */

{

int pid;

int p[2], q[2];

FILE *fdopen(), *fp;

int c;

int newline = TRUE;

int total;

 

/* Установка программных каналов p и q */

pipe(p);

pipe(q);

/* p[R], q[R] - концы каналов для чтения */

/* p[W], q[W] - концы каналов для записи */

switch(pid = fork())

{

case 0: /* ПОТОМОК */

/* читает из p[R] */

/* пишет в q[W] */

/* p[W] и q[R] - закрыты */

/* станд. ввод и p[R] - синонимы */

/* станд. вывод и q[W] синонимы */

/* Канал P */

close(p[W]);

close(R); dup(p[R]); close(p[R]);

/* Теперь станд. ввод и p[R] - синонимы */

/* Канал Q */

close(q[R]);

close(W); dup(q[W]); close(q[W]);

 

/* Теперь станд. вывод и q[W] - синонимы */

/* Запуск внешней независимой программы Count */

execl("count", "count", 0);

printf("textcount: Ошибка при вызове");

exit(1);

case -1: /* Cбой при вызове fork() */

printf("Ошибка при вызове fork() \n");

exit(1);

default: /* Это ПРЕДОК */

/* Конец канала P преобразуется для */

/* записи в поток */

close(p[R]); close(q[W]);

 

fp = fdopen(p[W], "w");

/* Посылка текстового файла в процесс COUNT */ while((c=getchar() != EOF )

{

switch(newline)

{

case TRUE:

if (c == '\n') /* Пустая строка */

putc(c, fp);

else if (c == PERIOD)

/* Пропустить строку */

while((c=getchar() != EOF && c != '\n') ;

else

{

putc(c, fp);

newline = FALSE;

}

break;

default:

putc(c, fp);

if (c == '\n')

newline = TRUE;

}

}

fclose(fp); /* Чтобы принимающий процесс мог воспринимать */

/* EOF на конце канала для чтения */

/* Теперь подключаем ввод результата */

/* из канала Q */

close(R); dup(q[R]); close(q[R]);

scanf("%d", &total);

printf("Общее число знаков %d\n", total);

exit(0);

}

}

 

/* Независимая программа Count – подсчет символов, полученных от процесса Textcount */

 

void main(void) /* COUNT.C */

/* Процесс, выполняющий COUNT, должен */

/* переназначить ввод и вывод так, */

/* чтобы ввод данных выполнялся из канала P, */

/* а вывод - канал Q */

/* */

{

int count = 0;

while (getchar() != EOF)

count++; printf("%d\n", count);

}