Создание порожденного процесса
Для создания порожденного процесса используется функция fork:
pid_t fork (void).
Тип pid_t определен в <sys/types.h> и соответствует типу int. При успешном выполнении создается порожденный процесс и функция возвращает идентификатор этого порожденного процесса родительскому процессу. Порожденный процесс получает от fork нулевой код возврата.
Как порожденный процесс, так и родительский планируются ядром Unix для выполнения независимо, а очередность запуска этих процессов зависит от реализации ОС. По завершении вызова fork выполнение обоих процессов возобновляется. Возвращаемое значение вызова fork мспользуется для того, чтобы определить, является ли процесс порожденным или родительским.
Таким образом, родительский и порожденный процессы могут выполнять различные задачи одновременно.
Функция exit завершает процесс. Ее прототип имеет вид:
void exit (int exit_code),
где exit_code – код завершения процесса.
Простейший пример создания нового процесса:
#include <iostream.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{ int status;
if (fork())
{ cout << "Child process\n";
exit(0);
};
cout << "Parent process\n";
}
Родительский процесс использует функции wait и waitpid для перехода в режим ожидания завершения порожденного процесса и для выборки его статуса завершения. Прототипы этих функций выглядят так:
pid_t wait (int status_p);
pid_t waitpid (pid_t child_pid, int* status_p,
int options).
Функция wait приостанавливает выполнение родительского процесса до тех пор, пока один из его порожденных процессов не завершится. Если порожденный процесс уже завершился, функция wait возвратится со статусом завершения порожденного процесса status_p, а возвращаемым значением будет PID порожденного процесса.
Функция waitpid является более универсальной, в ней с помощью аргумента child_pid можно указать, завершения какого из порожденных процессов следует ожидать.
Для того, чтобы вызывающий процесс изменил свой контекст (регистры, адресное пространство и др.) и выполнил другую программу, используется функция exec. Есть несколько версий этой функции, одна из них execl:
int execl (const char* path, const char* arg, …).
Первый аргумент функции является либо полным путевым именем, либо именем файла программы, подлежащей исполнению. Аргументы arg являются аргументами для программы, вызванной функцией exec. Они отображаются в переменную argv функции main новой программы. При этом arg отображается в argv[0], значение, указанное после arg, - в argv[1] и т. д. Список аргументов заканчивается нулевым значением.
Пример.
#include <stdio.h>
#include <unistd.h>
int main()
{
char s1[8]="string1", s2[4]="string2";
execl ("./b.out", s1, s2, 0);
return 0;
}
Здесь вызывается программа b.out, скомпилированная из файла
#include <stdio.h>
int main(int argc, char* argv[])
{
printf("%s\n",argv[0]);
printf("%s\n",argv[1]);
return 0;
}
В результате выполнения примера будут выведены строки string1
string2
Большинство программ вызывают exec в порожденном процессе, так как выполнение родительского процесса после вызова exec желательно продолжить. Однако программа может вызвать exec без fork, аfork без exec.
Программные каналы
Функция pipe создает прораммный канал между двумя взаимосвязанными процессами. В частности эта функция создает файл канала, который служит в качестве временного буфера и используется для того, чтобы вызывающий процесс мог записывать и считывать данные другого процесса. Файлу канала имя не присваивается, поэтому он называется неименованным каналом. Прототип функции:
int pipe (int fifo[2]).
Аргумент fifo является массивом, состоящим из двух целых чисел, присваиваемых ему интерфейсом pipe. В болшинстве систем Unix канал является однонаправленным, т. е. для чтения данных из канала процесс использует дескриптор файла fifo[0], а для записи данных в канал – другой дескриптор файла fifo[1].
Ниже приводится пример, в котором вызывающий процесс передает другому процессу через канал вещественное число, которое обрабатывается другим процессом и передается обратно.
#include <iostream.h>
#include <stdio.h>
#include <sys/wait.h>
#include <unistd.h>
int main()
{
int child_pid;
int fifo[2], status;
int sf=sizeof(float);
float buf1=2;
pipe(fifo);
write(fifo[1],&buf1,sf);
child_pid=fork();
if (child_pid==0)
{ float buf2;
read(fifo[0],&buf2,sf);
buf2=buf2+1;
write(fifo[1],&buf2,sf);
exit(0);
};
wait(&status);
read(fifo[0],&buf1,sf);
cout << "buf1=" << buf1;
}
.