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