Базовые средства организации и управления процессами

Второе определение процесса Unix.

Системный вызов – специальная функция, позволяющая процессу обращаться к ядру ОС за выполнением тех или иных действий.

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

Если системный вызов не выполняется или выполняется нештатно, то он возвращает –1 в коде ответа и в переменной errno будет находится код причины отказа (для диагностирования результатов выполнения системного вызова в процессе используется переменная errno, объявленная в файле errno.h).

Процесс в ОС Unix – это объект, порожденный системным вызовом fork().

Данный системный вызов является единственным стандартным средством порождения процессов в системе Unix.

Ниже рассмотрим возможности данного системного вызова подробнее.

Создание нового процесса

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

Для порождения новых процессов в UNIX существует единая схема, с помощью которой создаются все процессы, существующие в работающем экземпляре ОС UNIX, за исключением первых двух процессов (0-го и 1-го)

Для создания нового процесса в операционной системе UNIX используется системный вызов fork().

При удачном завершении возвращается:

- сыновьему процессу значение 0

- родительскому процессу PID порожденного процесса

При неудачном завершении возвращается –1, код ошибки устанавливается в переменной errno

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

Сыновний процесс наследует от родительского процесса:

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

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

способы обработки сигналов;

разрешение переустановки эффективного идентификатора пользователя;

разделяемые ресурсы процесса-отца;

текущий рабочий каталог и домашний каталоги

 и т.д.

Не наследуются от родительского процесса:

- Идентификатор процесса (PID)

- Идентификатор родительского процесса (PPID)

- Сигналы, ждущие доставки в родительский процесс

- Время посылки ожидающего сигнала, установленное системным вызовом alarm()

- Блокировки файлов, установленные родительским процессом

По завершении системного вызова fork() каждый из процессов – родительский и порожденный – получив управление, продолжат выполнение с одной и той же инструкции одной и той же программы, а именно с той точки, где происходит возврат из системного вызова fork(). Вызов fork() в случае удачного завершения возвращает сыновнему процессу значение 0, а родительскому PID порожденного процесса. Это принципиально важно для различения сыновнего и родительского процессов, так как сегменты кода у них идентичны. Таким образом, у программиста имеется возможность разделить путь выполнения инструкций в этих процессах. В случае неудачного завершения, т.е. если сыновний процесс не был порожден, системный вызов fork() возвращает –1, код ошибки устанавливается в переменной errno.

Пример

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

int main(int argc, char **argv)

{

printf("PID=%d; PPID=%d \n",getpid(), getppid());

/*печать PID текущего процесса и PID процесса-предка */

fork();

/*создание нового процесса, с этого момента два процесса функционируют параллельно и независимо*/

printf("PID=%d; PPID=%d \n",getpid(), getppid());

/*оба процесса печатают PID текущего процесса и PID процесса-предка*/

return 0;

}

Напечатается две строки, какая первой – неизвестно. Какой идентификатор родительского процесса распечатает вновь созданный процесс, если процесс-предок завершит свою работу раньше? (см. ответ дальше по тексту)

Пример

Программа создает два процесса – процесс-предок распечатывает заглавные буквы, а процесс-потомок строчные.

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

int main(int argc, char **argv)

{

char ch, first, last;

int pid;

if((pid=fork())>0)

{

/*процесс-предок*/

first =’A’;

last =’Z’;

}

else

{

/*процесс-потомок*/

first =’a’;

last =’z’;

}

for (ch = first; ch <= last; ch++)

{

write(1,&ch,1);

}

return 0;

}

Оба процесса распечатывают буквы одним и тем же оператором for.Оба процесса имеют возможность получить управление, таким образом любой из них может начать исполнение первым.

Семейство системных вызовов exec()

Ниже представлены прототипы функций семейства exec():

#include <unistd.h>

int execl(const char *path, char *arg0,…);

int execlp(const char *file, char *arg0,…);

int execle(const char *path, char *arg0,…, const char **env);

int execv(const char *path, const char **arg);

int execvp(const char *file, const char **arg);

int execve(const char *path, const char **arg, const char **env);

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

Первый параметр во всех вызовах задает имя файла программы, подлежащей исполнению. Этот файл должен быть исполняемым файлом и пользователь-владелец процесса должен иметь право на исполнение данного файла. Для функций с суффиксом «p» в названии имя файла может быть кратким, при этом при поиске нужного файла будет использоваться переменная окружения PATH. Далее передаются аргументы командной строки для вновь запускаемой программы, которые отобразятся в ее массив argv – в виде списка аргументов переменной длины для функций с суффиксом «l» либо в виде вектора строк для функций с суффиксом «v». В любом случае, в списке аргументов должно присутствовать как минимум 2 аргумента: имя программы, которое отобразится в элемент argv[0], и значение NULL, завершающее список.

В функциях с суффиксом «e» имеется также дополнительный аргумент, описывающий переменные окружения для вновь запускаемой программы – это массив строк вида name=value, завершенный значением NULL.

Возвращается: при удачном завершении 0, в случае ошибки -1

Заметим, что выполнение “нового” тела происходит в рамках уже существующего процесса, т.е. после вызова exec() сохраняется идентификатор процесса, и идентификатор родительского процесса, таблица дескрипторов файлов, приоритет, и большая часть других атрибутов процесса. Фактически происходит замена сегмента кода и сегмента данных.

Изменяются следующие атрибуты процесса:

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

- эффективные идентификаторы владельца и группы могут измениться, если для новой выполняемой программы установлен s-бит

- перед началом выполнения новой программы могут быть закрыты некоторые файлы, ранее открытые в процессе. Это касается тех файлов, для которых при помощи системного вызова fcntl() был установлен флаг close-on-exec. Соответствующие файловые дескрипторы будут помечены как свободные.

Сохраняются:

•Идентификатор процесса

•Идентификатор родительского процесса

•Таблица дескрипторов файлов

•Приоритет и большинство атрибутов


Понравилась статья? Добавь ее в закладку (CTRL+D) и не забудь поделиться с друзьями:  



double arrow
Сейчас читают про: