Системное программное обеспечение - Учебное пособие (Терехин А.Н.)

3.4      порождение новых процессов.

Для порождения новых процессов в UNIX существует единая схема, с помощью которой создаются все процессы, существующие в работающем экземпляре ОС UNIX, за исключением процессов с PID=0 и PID=1[5].

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

#include <sys/types.h>

#include <unistd.h>

pid_t fork(void);

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

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

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

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

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

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

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

-     и т.д.

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

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

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

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

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

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

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

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

Рис. 9 Выполнение системного вызова fork()

Порождение сыновнего процесса. Идентификаторы процессов.

#include <sys/types.h>

#include <unistd.h>

#include <stdio.h>

int main(int argc, char **argv)

{

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

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

fork();                        

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

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

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

return 0;                    

}

В этом примере оба процесса узнают свой собственный идентификатор процесса с помощью вызова getpid(), а идентификатор родительского процесса – с помощью вызова getppid().

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

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

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

#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. Оба процесса имеют возможность получить управление, таким образом любой из них может начать исполнение первым.