/* передается свое окружение */
execv("/bin/ps", ps_argv);
execvp("ps", ps_argv);
execve("/bin/ps", ps_argv, ps_envp);
А теперь выполните упражнение 11.2.
Упражнение 11.2. Функция execlp
Давайте изменим пример и используем вызов execlp
:
#include
#include
#include
int main() {
printf("Running ps with execlp\n");
execlp("ps", "ps", "ax", 0);
printf("Done.\n");
exit(0);
}
Когда вы выполните эту программу, рехес.с, то получите обычный вывод команды ps
, но без сообщения Done
. Кроме того, обратите внимание на то, что в выводе нет процесса с именем рехес
:
$ ./рехес
Running ps with execlp
PID TTY STAT TIME COMMAND
1 ? S 0:03 init [5]
...
1262 pts/1 Ss 0:00 /bin/bash
1273 pts/2 S 0:00 su -
1274 pts/2 S+ 0:00 -bash
1463 pts/1 SN 0:00 oclock
1465 pts/1 S 0:01 emacs Makefile
1514 pts/1 R+ 0:00 ps ax
Как это работает
Программа выводит первое сообщение и затем вызывает функцию execlp
, которая ищет каталоги, заданные в переменной окружения PATH
для обнаружения программы ps
. Далее она выполняет команду вместо программы рехес
, запустив ее так, как будто вы ввели команду командной оболочки
$ ps ax
Когда ps
завершается, вы получаете новую строку приглашения командной оболочки. Возврата в программу рехес
не происходит, поэтому второе сообщение не выводится. PID нового процесса тот же, что и у исходного, то же самое можно сказать о PID родительского процесса и значении nice
. В сущности, происходит следующее: выполняющаяся программа запустила на выполнение новый код и новый исполняемый файл, заданный в вызове функции exec
.
Существует ограничение для общего размера списка аргументов и окружения процесса, запускаемого функциями exec
. Оно задается в переменной ARG_MAX
и в системах Linux равно 128 Кбайт. В других системах может задаваться меньший предельный размер, что способно порождать проблемы. Стандарт POSIX гласит, что ARG_MAX
должна быть не менее 4096 байтов.
Функции exec
, как правило, не возвращаются в программу до тех пор, пока не возникла ошибка, в этом случае задается переменная errno
и функция exec
возвращает -1.
Новые процессы, запущенные exec, наследуют многие свойства исходного процесса. В частности, открытые файловые дескрипторы остаются открытыми в новом процессе, пока не установлен их флаг FD_CLOEXEC
(close on exec) (подробную информацию см. в описании системного вызова fcntl
в главе 3 ). Любые открытые в исходном процессе потоки каталогов закрываются.
Дублирование образа процесса
Для применения процессов, выполняющих несколько функций одновременно, можно либо использовать потоки, обсуждаемые в главе 12, либо создавать в программе полностью отдельный процесс, как делает init
, вместо замещения текущего потока исполнения, как в случае применения функции exec
.
Создать новый процесс можно с помощью вызова fork
. Системный вызов дублирует текущий процесс, создавая новый элемент в таблице процессов с множеством атрибутов, таких же как у текущего процесса. Новый процесс почти идентичен исходному, выполняет тот же программный код, но в своем пространстве данных, окружении и со своими файловыми дескрипторами. В комбинации с функциями exec
вызов fork
— все, что вам нужно для создания новых процессов.
#include
#include
pid_t fork(void);
Как видно из рис. 11.2, вызов fork
возвращает в родительский процесс PID нового дочернего процесса. Новый процесс продолжает выполнение так же, как и исходный, за исключением того, что в дочерний процесс вызов fork
возвращает 0. Это позволяет родительскому и дочернему процессам определить, "кто есть кто".
Рис. 11.2
Если вызов fork
завершается аварийно, он возвращает -1. Обычно это происходит из-за ограничения числа дочерних процессов, которые может иметь родительский процесс ( CHILD_MAX
), в этом случае переменной errno
будет присвоено значение EAGAIN
. Если для элемента таблицы процессов недостаточно места или не хватает виртуальной памяти, переменная errno
получит значение ENOMEM
.
Читать дальше