Лабораторная работа № 5. Работа с процессами в ОС Unix. Сигналы и каналы в Unix
1. Цель работы
Изучить программные средства создания процессов, получить навыки управления и синхронизации процессов, а также простейшие способы обмена данными между процессами. Ознакомиться со средствами динамического запуска программ в рамках порожденного процесса, изучить механизм сигналов ОС UNIX, позволяющий процессам реагировать на различные события, и каналы, как одно из средств обмена информацией между процессами. 2. Теоретические сведения
Для порождения нового процесса (процесс-потомок) используется системный вызов fork(). Формат вызова:
int fork();
Порожденный таким образом процесс представляет собой точную копию своего процесса-предка. Единственное различие между ними заключается в том, что процесс-потомок в качестве возвращаемого значения системного вызова fork() получает 0, а процесс-предок - идентификатор процесса-потомка. Кроме того, процесс-потомок наследует и весь контекст программной среды, включая дескрипторы файлов, каналы и т.д. Наличие у процесса идентификатора дает возможность и ОС UNIX, и любому другому пользовательскому процессу получить информацию о функционирующих в данный момент процессах.
Ожидание завершения процесса-потомка родительским процессом выполняется с помощью системного вызова wait():
int wait(int *status);
В результате осуществления процессом системного вызова wait() функционирование процесса приостанавливается до момента завершения порожденного им процесса-потомка. По завершении процесса-потомка процесс-предок пробуждается и в качестве возвращаемого значения системного вызова wait() получает идентификатор завершившегося процесса-потомка, что позволяет процессу-предку определить, какой из его процессов-потомков завершился (если он имел более одного процесса-потомка). Аргумент системного вызова wait() представляет собой указатель на целочисленную переменную status, которая после завершения выполнения этого системного вызова будет содержать в старшем байте код завершения процесса-потомка, установленный последним в качестве системного вызова exit(), а в младшем - индикатор причины завершения процесса-потомка.
Формат системного вызова exit(), предназначенного для завершения функционирования процесса:
int exit(int status);
Аргумент status является статусом завершения, который передается отцу процесса, если он выполнял системный вызов wait().
Для получения собственного идентификатора процесса используется системный вызов getpid(), а для получения идентификатора процесса-отца - системный вызов getppid():
int getpid();
int getppid();
Вместе с идентификатором процесса каждому процессу в ОС UNIX ставится в соответствие также идентификатор группы процессов. В группу процессов объединяются все процессы, являющиеся процессами-потомками одного и того же процесса. Организация новой группы процессов выполняется системным вызовом getpgrp(), а получение собственного идентификатора группы процессов - системным вызовом getpgrp(). Их формат:
int setpgrp();
int getpgrp();
С практической точки зрения в большинстве случаев в рамках порожденного процесса загружается для выполнения программа, определенная одним из системных вызовов execl(), execv(),... Каждый из этих системных вызовов осуществляет смену программы, определяющей функционирование данного процесса:
execl(name, arg0, arg1, ... , argn, 0)
char *name, *arg0, *arg1, ... , *argn;
execv(name, argv)
char *name, *argv[];
execle(name, arg0, arg1, ... , argn, 0, envp)
char *name, *arg0, *arg1, ... , *argn, *envp[];
execve(name, argv, envp)
char *name, *arg[], *envp[];
Сигналы - это программное средство, с помощью которого может быть прервано функционирование процесса в ОС UNIX. Механизм сигналов позволяет процессам реагировать на различные события, которые могут произойти в ходе функционирования процесса внутри него самого или во внешнем мире. Каждому сигналу ставятся в соответствие номер сигнала и строковая константа, используемая для осмысленной идентификации сигнала. Эта взаимосвязь отображена в файле описаний signal.h. Для посылки сигнала используется системный вызов, имеющий формат:
void kill(int pid, int sig);
В результате осуществления такого системного вызова сигнал, специфицированный аргументом sig, будет послан процессу, который имеет идентификатор pid. Если pid не превосходит 1, сигнал будет послан целой группе процессов.
Использование системного вызова signal() позволяет процессу самостоятельно определить свою реакцию на получение того или иного события (сигнала):
int sig;
int (*func)();
int *signal(sig, func) ();
Реакцией процесса, осуществившего системный вызов signal() с аргументом func, при получении сигнала sig будет вызов функции func().
Системный вызов pause() позволяет приостановить процесс до тех пор, пока не будет получен какой-либо сигнал:
void pause();
Системный вызов alarm(n) обеспечивает посылку процессу сигнала SIGALARM через n секунд.
В ОС UNIX существует специальный вид взаимодействия между процессами - программный канал. Программный канал создается с помощью системного вызова pipe(), формат которого:
int fd[2];
pipe(fd);
Системный вызов pipe() возвращает два дескриптора файла: один для записи данных в канал, другой - для чтения. После этого все операции передачи данных выполняются с помощью системных вызовов ввода-вывода read/write. При этом система ввода-вывода обеспечивает приостановку процессов, если канал заполнен (при записи) или пуст (при чтении). Таких программных каналов процесс может установить несколько. Отметим, что установление связи через программный канал опирается на наследование файлов. Взаимодействующие процессы должны быть родственными. 3. Оборудование
Персональный компьютер с установленной операционной системой Unixили Linux. 4. Задание на работу
Изучить правила использования системных вызовов fork(), wait(), exit(). Ознакомиться с системными вызовами getpid(), getppid(), sеtpgrp(), getpgrp(). Изучить средства динамического запуска программ в ОС UNIX (системные вызовы execl(), execv(),...). Изучить средства работы с сигналами и каналами в ОС UNIX. Ознакомиться с заданием к лабораторной работе. Для указанного варианта составить программу на языке Си, реализующую задание. Отладить и оттестировать составленную программу, используя инструментарий ОС UNIX. Защитить лабораторную работу, ответив на контрольные вопросы.
Варианты заданий:
Разработать программу, вычисляющую интеграл на отрезке [A;B] от функции exp(x) методом трапеций, разбивая интервал на K равных отрезков. Для нахождения exp(х) программа должна породить процесс, вычисляющий её значение путём разложения в ряд по формулам вычислительной математики. Разработать программу, вычисляющую значение f(x) как сумму ряда от k=0 до k=N от выражения x^(2k+1)/(2k+1)! для значений x, равномерно распределённых на интервале [0;Pi], и выводящую полученный результат f(x) в файл в двоичном формате. В это время предварительно подготовленный процесс-потомок читает данные из файла, преобразовывает их в текстовую форму и выводит на экран до тех пор, пока процесс-предок не передаст ему через файл ключевое слово (например, "STOP"), свидетельствующее об окончании процессов. Разработать программу, вычисляющую плотность распределения Пуассона с параметром lambda в точке k (k - целое) по формуле f(k)=lambda^k*exp(-lambda)/k!. Для нахождения факториала и exp(-lambda) программа должна породить два параллельных процесса, вычисляющих эти величины путём разложения в ряд по формулам вычислительной математики. Разработать программу, вычисляющую плотность выпуклого распределения в точке х по формуле f(x)=(1-cos(x))/(Pi*x^2). Для нахождения Pi и cos(х) программа должна породить два параллельных процесса, вычисляющих эти величины путём разложения в ряд по формулам вычислительной математики. Разработать программу, вычисляющую значение плотности лог-нормального распределения в точке х (x>0) по формуле f(x)=(1/2)*exp(-(1/2)*ln(x)^2)/(x*sqrt(2*Pi)). Для нахождения Pi, exp(x) и ln(x) программа должна породить три параллельных процесса, вычисляющих эти величины путём разложения в ряд по формулам вычислительной математики. Разработать программу, вычисляющую число размещений n элементов по r ячейкам N=n!/n(1)!*n(2)!*...*n(r)!, удовлетворяющее требованию, что в ячейку с номером i попадает ровно n(i) элементов (i=1..r) и n(1)+n(2)+...+n(r)=n. Для вычисления каждого факториала необходимо породить процесс-потомок. Разработать программу, вычисляющую число сочетаний C(k,n)=n!/(k!*(n-k)!). Для вычисления факториалов n!, k!, (n-k)! должны быть порождены три параллельных процесса-потомка. Разработать программу, вычисляющую значение f(x) как сумму ряда от k=1 до k=N от выражения (-1)^(k+1)*x^(2k-1)/(2k-1)! для значений x, равномерно распределённых на интервале [0;Pi], и выводящую полученный результат f(x) в файл в двоичном формате. В это время предварительно подготовленный процесс-потомок читает данные из файла, преобразовывает их в текстовую форму и выводит на экран до тех пор, пока процесс-предок не передаст ему через файл ключевое слово (например, "STOP"), свидетельствующее об окончании процессов. Разработать программу, вычисляющую плотность нормального распределения в точке x по формуле f(x)=exp(-x^2/2)/sqrt(2*Pi). Для нахождения Pi и exp(-x^2/2) программа должна породить два параллельных процесса, вычисляющих эти величины путём разложения в ряд по формулам вычислительной математики. Разработать программу, вычисляющую интеграл в диапазоне от 0 до 1 от подинтегрального выражения 4*dx/(1+x^2) с помощью последовательности равномерно распределённых на отрезке [0;1] случайных чисел, которая генерируется процессом-потомком параллельно. Процесс-потомок должен завершиться после заранее заданного числа генераций N.
5. Оформление отчета
Отчет должен содержать:
название и цель работы; вариант задания; краткие теоретические положения и алгоритм работы программы результаты работы созданного программного обеспечения; листинг программы, реализующей задание.
6 Контрольные вопросы
6.1. Каким образом может быть порожден новый процесс? Какова структура нового процесса?
6.2. Если процесс-предок открывает файл, а затем порождает процесс-потомок, а тот, в свою очередь, изменяет положение указателя чтения-записи файла, то изменится ли положение указателя чтения-записи файла процесса-отца?
6.3. Что произойдет, если процесс-потомок завершится раньше, чем процесс-предок осуществит системный вызов wait()?
6.4. Могут ли родственные процессы разделять общую память?
6.5. Каков алгоритм системного вызова fork()?
6.6. Какова структура таблиц открытых файлов, файлов и описателей файлов после создания процесса?
6.7. Каков алгоритм системного вызова exit()?
6.8. Каков алгоритм системного вызова wait()?
6.9. В чем разница между различными формами системных вызовов типа exec()?
6.10. Для чего используются сигналы в ОС UNIX?
6.11. Какие виды сигналов существуют в ОС UNIX?
6.12. Для чего используются каналы?
6.13. Какие требования предъявляются к процессам, чтобы они могли осуществлять обмен данными посредством каналов?
6.14. Каков максимальный размер программного канала и почему? 7. Библиографический список
1. Забродин Л.Д. UNIX. Введение в командный интерфейс. – М.: ДИАЛОГ-МИФИ, 1994. – 144 с.
2. Дансмур М., Дейвис Г. Операционная система UNIX и программирование на языке Си: Пер. с англ. – М.: Радио и связь, 1989. – 192 с.
3. Керниган Б.В., Пайк Р. UNIX – универсальная среда программирования: Пер. с англ. – М.: Финансы и статистика, 1992. – 304 с.
4. Робачевский А.М. Операционная система UNIX. – СПб.: BHV – Санкт-Петербург, 1997. – 528 с.
5. Т.Чан Системное программирование на C++ для UNIX. /Пер. с англ. -К.: Издательская группа BHV, 1997. - 592 с.
|