Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

ОАиП / Лаб раб _8

.doc
Скачиваний:
23
Добавлен:
01.03.2016
Размер:
52.22 Кб
Скачать

Лабораторная работа №8

«Управление процессами в OC Linux»

Задание:

  1. Напишите программу, которая вызывает программу, написанную в лаб.р.7.

  2. Создайте программу, которая поддерживает параллельный запуск. При этом «сын» получает данные у пользователя (строка или число) и выводит в общий файл, а процесс «родитель» выводит данные из файла на экран.

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

В операционной системе ДЕМОС часто требуется вызвать из программы и выполнить в виде отдельного процесса другую программу. Следующий раздел описывает простейший способ сделать это, а далее будут рассмотрены базовые средства управления процессами, имеющиеся в ОС ДЕМОС.

Функция system()

Простейший способ вызвать другую программу - использовать стандартную функцию system():

system("командная строка")

Функция имеет один параметр - строку, которую она анализирует и выполняет точно так же, как выполняются команды, вводимые интерпретатором shell с терминала. Функция выполняет команду и возвращает целое число - код ответа выполненной команды (0, если все кончилось нормально). В командной строке воспринимаются любые символы управления вводом/выводом >, <, и т.п.

Следует учесть, что, если в программе вывод буферизуется, то перед вызовом функции system() необходимо вытолкнуть буфера, например вызвав функцию fflush().

Вызов программы на низком уровне – execl()

Вызов программы в ОС ДЕМОС осуществляется с помощью нескольких элементарных функций, одна из которых – функция execl() - осуществляет вызов новой программы вместо уже выполняющейся, без возврата в вызывающую программу. Обращение в ней имеет вид:

execl(команда,арг0,арг1,...,аргN,NULL);

где «команда» - строка символов, точно именующая файл вызываемой команды. Например, для вызова команды pr необходимо указать имя /bin/pr. Остальные аргументы также представляют собой строки символов и просто передаются команде в качестве аргументов, при этом арг0 обычно представляет собой просто сокращенное имя команды, а остальные аргументы – параметры данной команды.

Вызов execl() в случае нормального запуска новой программы заменяет ею текущую программу, управление из функции execl() возвращается только в случае ошибки (например, не найдена команда с указанным именем). В библиотеке имеется целый набор функций, осуществляющих то же самое и отличающихся только представлением параметров (execl(2), execv(2), execvp(2), ...) и тем, что некоторые функции осуществляют поиск команды в стандартном наборе справочников.

Порождение нового процесса – fork()

Для того, чтобы запустить параллельно новую программу, необходимо прежде всего уметь запускать параллельный процесс. Для этого в ОС ДЕМОС служит функция fork() (разветвиться):

proc_id = fork();

Программа разделяется на две идентичные копии, которые продолжают выполняться как два независимых процесса. Одна из программ - процесс «сын» - получает от функции fork() код ответа 0, другая – «родитель» - получает номер, под которым запущен процесс «сын». В простейшем случае для запуска параллельной программы вызов fork() комбинируется с execl() следующим образом:

if (fork() == 0)

{ /* Это процесс - сын */

... настройка файлов ...

exit(1);

}

... продолжение основной программы ...

Здесь программа после вызова fork() анализирует, в каком процессе («родитель» или «сын») она выполняется и, в зависимости от этого, ведет себя по разному. Если основная программа должна ждать окончания «сына», то она должна вызвать функцию wait():

int status;

...

if (fork() == 0)

{

...

exit(1);

}

wait(&status);

Функция wait() возвращает идентификатор процесса-сына, и засылает в переменную status код завершения этого процесса. Код завершения состоит из двух частей - младшие 8 битов формируются системой и обозначают причину окончания процесса; в случае нормального окончания по функции exit() они содержат 0. Старшие 8 битов в случае, если программа окончилась в результате вызова exit(), берутся из аргумента вызова функции exit(); обычно передается 0 при нормальном завершении и число, отличное от нуля, в случае каких либо ошибок.

Ни fork(), ни execl() не затрагивают открытых файлов, после fork() ранее открытые файлы начинают использоваться обоими процессами совместно, то есть используются одни и те же указатели позиции чтения/записи. Если новому процессу требуется передать какие-то открытые файлы, или изменить файлы стандартного ввода/вывода, настройка программы на эти файлы делается после вызова fork() в процессе-сыне до вызова execl(). Следует заметить, что при буферизованном вводе/выводе необходимо сбросить буфера перед вызовом fork(), иначе вывод накопленной информации может произойти дважды - и в «родительском», и в новом процессе.

Канал межпроцессной связи

Межпроцессный канал - это особый файл, устроенный таким образом, что один процесс неограниченно записывает в него информацию, а другой читает, причем система обеспечивает буферизацию данных и синхронизацию процессов. Межпроцессные каналы могут создаваться интерпретатором команд shell или cshell, например:

ls | pr

Существуют библиотечные функции popen() и pclose(), позволяющие запустить параллельный процесс, который будет читать информацию, записываемую в указанный файл данным процессом, или, напротив, будет поставлять породившему его процессу данные для чтения (см. popen(3)). Эти функции используют базовые возможности построения каналов, которые поддерживаются операционной системой.

Для создания канала межпроцессной связи служит функция pipe():

int fd[2];

...

stat = pipe(fd);

if(stat == -1) /* Была ошибка */

Здесь fd - массив, в который засылается два дескриптора файлов - fd[1] для записи в канал, fd[0] для чтения из канала. Эти дескрипторы могут использоваться наравне с дескрипторами обычных файлов.

Синхронизация обменов построена таким образом, что, если процесс читает пустой канал, он будет ждать появления данных; если в канале осталось много несчитанной информации, записывающий процесс будет ждать освобождения места в канале. Наконец, если у канала сторона для записи закрыта, при чтении будет получен код ответа «0» - конец файла.

Как правило, программа создает канал по запросу pipe(), после чего разделяется на две копии с помощью функции fork(). Затем в одном из получившихся процессов закрывается сторона канала для чтения, в другом - закрывается дескриптор записи в канал. Теперь после вызова execl() начинается обмен информацией по межпроцессному каналу между параллельно выполняющимися программами.

В случае, если обмен должен происходить через стандартный ввод или вывод, используется функция dup() для связывания дескрипторов файлов.

Пример 14.9.1. Следующий фрагмент программы служит для запуска программы pr так, чтобы данные на стандартный ввод программы pr поступали из стандартного вывода основной программы.

Листинг 14.9.1

#define R 0

#define W 1

int fd[2];

pipe(fd);

if (fork() == 0)

{

close(fd[W]);

close(0);

dup(fd[R]);

close(fd[R]);

exit(1);

}

close(fd[R]);

close(1);

dup(fd[W]);

close(fd[W]);

.... счет, при записи проверяем, не было

.... ли ошибки записи.

close(1);

В этом примере полностью опущена обработка возможных ошибок. Для связывания дескрипторов стандартного ввода или вывода с каналом межпроцессной связи здесь использована функция dup(fd), которая возвращает дубликат дескриптора fd, причем используется наименьший свободный дескриптор файла. Следовательно, после закрытия файла с дескриптором 0 ближайшее обращение к функции dup() свяжет дескриптор 0 с заданным в аргументе dup() дескриптором. После вызова dup() ненужный больше дескриптор fd[0] или fd[1] закрывают.