В DOS и Windows API имеется семейство функций spawn()
. Они принимают в качестве аргумента имя программы, создают новый экземпляр ее процесса и запускают его. В Linux нет функции, которая делала бы все это за один заход. Вместо этого имеется функция fork()
, создающая дочерний процесс, который является точной копией родительского процесса, и семейство функций exec()
, заставляющих требуемый процесс перестать быть экземпляром одной программы и превратиться в экземпляр другой программы. Чтобы создать новый процесс, нужно сначала с помощью функции fork()
создать копню текущего процесса, а затем с помощью функции exec()
преобразовать одну из копий в экземпляр запускаемой программы.
Вызывая функцию fork()
, программа создает свой дубликат, называемый fork()
. То же самое делает и дочерний процесс.
Как же различить между собой оба процесса? Во-первых, дочерний процесс — это новый, только что появившийся в системе процесс, поэтому его идентификатор отличается от идентификатора родительского процесса. Таким образом, программа может вызвать функцию getpid()
и узнать, где именно она находится. Но сама функция fork()
реализует другой способ: она возвращает разные значения в родительском и дочернем процессах. Родительский процесс получает идентификатор своего потомка, а дочернему процессу возвращается 0. В системе нет процессов с нулевым идентификатором, так что программа легко разбирается в ситуации.
В листинге 3.3 приведен пример ветвления программы с помощью функции fork()
. Учтите, что первая часть инструкции if
выполняется только в родительском процессе, тогда как ветвь else
— только в дочернем.
fork()
#include
#include
#include
int main() {
pid_t child_pid;
printf("The main program process ID is %d\n",
(int)getpid());
child_pid = fork();
if (child_pid != 0) {
printf("This is the parent process, with ID %d\n",
(int)getpid());
printf("The child's process ID is %d\n", (int)child_pid);
} else
printf("This is the child process, with ID %d\n",
(int)getpid());
return 0;
}
Функции семейства exec()
заменяют программу, выполняющуюся в текущем процессе, другой программой. Когда программа вызывает функцию exec()
, ее выполнение немедленно прекращается и начинает работу новая программа.
Функции, входящие в семейство exec()
, немного отличаются друг от друга по своим возможностям и способу вызова.
■ Функции, в названии которых присутствует суффикс 'p'
(execvp()
и execlp()
), принимают в качестве аргумента имя программы и ищут эту программу в каталогах, определяемых переменном среды PATH
. Всем остальным функциям нужно передавать полное путевое имя программы.
■ Функции, в названии которых присутствует суффикс 'v'
(execv()
, execvp()
и execve()
), принимают список аргументов программы в виде массива строковых указателей, оканчивающегося NULL
-указателем. Функции с суффиксом 'l'
(execl()
, execlp()
и execle()
) принимают список аргументов переменного размера.
■ Функции, в названии которых присутствует суффикс 'e'
(execve()
и execle()
), в качестве дополнительного аргумента принимают массив переменных среды. Этот массив содержит строковые указатели и оканчивается пустым указателем. Каждая строка должна иметь вид "
.
Поскольку функция exec()
заменяет одну программу другой, она никогда не возвращает значение — только если вызов программы оказался невозможен в случае ошибки.
Список аргументов, передаваемых программе, аналогичен аргументам командной строки, указываемым при запуске программы в интерактивном режиме. Их тоже можно получить с помощью параметров argc
и argv
функции main()
. Не забывайте, когда программу запускает интерпретатор команд, первый элемент массива argv будет содержать имя программы, а далее будут находиться переданные программе аргументы. Аналогичным образом следует поступить, формируя список аргументов для функции exec()
.
Стандартная методика запуска одной программы из другой такова: сначала с помощью функции fork()
создается дочерний процесс, затем в нем вызывается функция exec()
. Это позволяет главной программе продолжать выполнение в родительском процессе.