logo
Операционные системы

Пример использования системного вызова fork().

Рассмотрим еще один пример. В данном случае используются дополнительно два системных вызова: getpid() для получения идентификатора текущего процесса и getppid() для получения идентификатора родительского процесса. Итак, данный процесс при запуске печатает на экране идентификаторы себя и своего отца, затем производит обращение к системному вызову fork(), после чего и данный процесс, и его потомок снова печатают идентификаторы. Соответственно, на экране в случае успешной обработки всех системных вызовов будут напечатаны три строки.

int main(int argc, char **argv)

{

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

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

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

fork();

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

независимо*/

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

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

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

}

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

#include <unistd.h>

int execl(const char *path, char *arg0, ..., char *argn, 0);

Параметр path указывает на имя исполняемого файла. Параметры arg0, …, argn являются аргументами программы, передаваемые ей при вызове (это те параметры, которые будут содержаться в массиве argv при входе в программу). При неудачном завершении возвращается -1, а в переменной errno устанавливается код ошибки.

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

Рассмотрим небольшой пример (Рис. 77.). Запускается процесс (ему ставится в соответствие идентификатор 2757), который обращается к системному вызову execl(), для смены своего тела телом команды ls -l, которая отображает содержимое текущего каталога. Реализация данной команды хранится, соответственно, в файле /bin/ls. После успешного завершения системного вызова execl() процесс (с тем же идентификатором 2757) будет содержать реализацию команды ls, и управление в нем будет передано на точку входа (т.е. запустится функция main()).

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