Управление процессами в ОС UNIX
Цель работы
Ознакомиться со средствами организации взаимодействия процессов в ОС UNIX.
Содержание работы
- Ознакомиться со средствами организации взаимодействия процессов в ОС UNIX.
- Для указанного варианта составить следующие программы на языке Си:
- реализующую конвейер из двух фильтров (формулировка одного фильтра задана, второй фильтр необходимо придумать);
- организующую взаимодействие двух процессов через неименованный программный канал;
· организующую взаимодействие двух процессов через именованный программный канал.
Примечание. Проиллюстрировать работу неименованного программного канала и именованного программного канала на макетных программах с применением фильтров, используемых в задаче №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);
}