Scroll to navigation

POSIX_SPAWN(3) Руководство программиста Linux POSIX_SPAWN(3)

ИМЯ

posix_spawn, posix_spawnp - порождает процесс

СИНТАКСИС

#include <spawn.h>
int posix_spawn(pid_t *pid, const char *path,
                const posix_spawn_file_actions_t *file_actions,
                const posix_spawnattr_t *attrp,
                char *const argv[], char *const envp[]);
int posix_spawnp(pid_t *pid, const char *file,
                 const posix_spawn_file_actions_t *file_actions,
                 const posix_spawnattr_t *attrp,
                 char *const argv[], char *const envp[]);

ОПИСАНИЕ

Функции posix_spawn() и posix_spawnp() используются для создания новых дочерних процессов, которые выполняют указываемый файл. Эти функции были определены в POSIX для стандартизации метода создания новых процессов на машинах, у которых нет возможности поддержки системного вызова fork(2). К таким машинах, обычно, относятся встраиваемые системы без поддержки MMU.

Функции posix_spawn() и posix_spawnp() предоставляют комбинацию возможностей fork(2) и exec(3) с некоторыми необязательными обслуживающими действиями в дочернем процессе перед exec(3). Эти функции не служат заменой системных вызовов fork(2) и execve(2). Фактически, они предоставляют только часть функций системных вызовов.

Единственным отличием между posix_spawnp() и posix_spawnp() является способ, которым в них указывается исполняемый дочерним процессом файл. В posix_spawn() исполняемый файл задаётся в виде пути (которое может быть абсолютным или относительным). В posix_spawnp() исполняемый файл задаётся в виде имени файла; система ищет этот файл в списке каталогов, указанных в PATH (также, как это делает execvp(3)). Кроме данного отличия далее на этой странице всё описание posix_spawn() также относится и к posix_spawnp().

Остальные аргументы функций:

  • Аргумент pid указывает на буфер, в котором возвращается ID нового дочернего процесса.
  • Аргумент file_actions указывает на объект файловых действий при создании, в котором задаются действия с файлом, выполняемые в потомке между шагами fork(2) и exec(3). Данный объект инициализируется и заполняется перед вызовом posix_spawn() с помощью функций posix_spawn_file_actions_init(3) и posix_spawn_file_actions_*().
  • Аргумент attrp указывает на объект атрибутов, в котором задаются различные атрибуты создаваемого дочернего процесса. Данный объект инициализируется и заполняется перед вызовом posix_spawn() с помощью функций posix_spawnattr_init(3) и posix_spawnattr_*().
  • В аргументах argv и envp задаётся список аргументов и окружения для программы, выполняемой в дочернем процессе, как для execve(2).

Далее функции описаны в виде трёх ступенчатого процесса: шаг fork(), шаг перед exec() (выполняется в потомке) и шаг exec() (выполняется в потомке).

Шаг fork()

Since glibc 2.24, the posix_spawn() function commences by calling clone(2) with CLONE_VM and CLONE_VFORK flags. Older implementations use fork(2), or possibly vfork(2) (see below).

PID нового дочернего процесса помещается в *pid. После этого функция posix_spawn() возвращает управление родительскому процессу.

Соответственно, родитель может использовать один из системных вызовов, описанных в wait(2), для проверки состояния дочернего процесса. Если потомок завершится с ошибкой в любом из служебных шагов, описанных далее, или возникнет ошибка при выполнении желаемого файла, то он завершит работу с кодом состояния 127.

Before glibc 2.24, the child process is created using vfork(2) instead of fork(2) when either of the following is true:

  • элемент spawn-flags объекта атрибутов, на который указывает attrp, содержит определённый в GNU флаг POSIX_SPAWN_USEVFORK; или
  • file_actions равно NULL и элемент spawn-flags объекта атрибутов, на который указывает attrp, не содержит POSIX_SPAWN_SETSIGMASK, POSIX_SPAWN_SETSIGDEF, POSIX_SPAWN_SETSCHEDPARAM, POSIX_SPAWN_SETSCHEDULER, POSIX_SPAWN_SETPGROUP или POSIX_SPAWN_RESETIDS.

Иначе говоря, vfork(2) используется, если это запросил вызывающий или не нужна очистка в потомке перед выполнением exec(3) запрашиваемого файла.

Шаг перед exec(): служебные действия

In between the fork() and the exec() steps, a child process may need to perform a set of housekeeping actions. The posix_spawn() and posix_spawnp() functions support a small, well-defined set of system tasks that the child process can accomplish before it executes the executable file. These operations are controlled by the attributes object pointed to by attrp and the file actions object pointed to by file_actions. In the child, processing is done in the following sequence:

1.
Действия с атрибутами процесса: маска сигналов, обработчики сигналов по умолчанию, алгоритм планирования и параметры, ID группы процесса, эффективного пользователя и группы изменяются согласно объекту атрибутов, на который указывает attrp.
2.
Файловые действия, указываемые в аргументе file_actions, выполняются в порядке их определения вызовами функций posix_spawn_file_actions_add*().
3.
Закрываются файловые дескрипторы, имеющие флаг FD_CLOEXEC.

Все атрибуты процесса-потомка, отличные от атрибутов в объекте, на который указывает attrp и файловые действия в объекте, на который указывает file_actions, будут изменены как если бы потомок создавался с помощью fork(2) и выполнял программу с помощью execve(2).

Действия атрибутов процесса определяются атрибутами объекта, на который указывает attrp. Атрибут spawn-flags (устанавливается с помощью posix_spawnattr_setflags(3)) управляет общими действиями, а остальные атрибуты объекта хранят значения, которые будут использованы в этих действиях.

Влияние флагов, которые могут быть указаны в spawn-flags:

Назначить маску сигналов равной набору сигналов, определённой в атрибуте spawn-sigmask объекта, на который указывает attrp. Если не установлен флаг POSIX_SPAWN_SETSIGMASK, то потомок наследует маску сигналов родителя.
Сбрасывает обработчики всех сигналов в наборе, заданном в атрибуте spawn-sigdefault объекта, на который указывает attrp, в значения по умолчанию. О том, что происходит с обработчиками сигналов не указанных в атрибуте spawn-sigdefault или когда не указан POSIX_SPAWN_SETSIGDEF, смотрите execve(2).
Если этот флаг установлен, а POSIX_SPAWN_SETSCHEDULER нет, то изменяет параметры планирования на значения, указанные в атрибуте spawn-schedparam объекта, на который указывает attrp.
Назначает алгоритм планирования и параметры потомка:
  • Алгоритму планирования присваивается значение, указанное в атрибуте spawn-schedpolicy объекта, на который указывает attrp.
  • Параметрам планирования присваивается значение, указанное в атрибуте spawn-schedparam объекта, на который указывает attrp (но смотрите ДЕФЕКТЫ).

Если не указаны флаги POSIX_SPAWN_SETSCHEDPARAM и POSIX_SPAWN_SETSCHEDPOLICY, то потомок наследует соответствующие атрибуты планирования от родителя.

Если этот флаг установлен, то сбрасываются эффективный UID и GID в реальный UID и GID родительского процесса. Если флаг не установлен, то потомок сохраняет эффективный UID и GID родителя. В любом случае, если биты прав set-user-ID и set-group-ID включены на исполняемом файле, то это заменяет значения эффективного UID и GID (смотрите execve(2)).
Назначает группе процесса значение, указанное в атрибуте spawn-pgroup объекта, на который указывает attrp. Если атрибут spawn-pgroup равен 0, то ID группы потомка становится равным его ID процесса. Если флаг POSIX_SPAWN_SETPGROUP не установлен, то потомок наследует ID группы процесса родителя.
Since glibc 2.24, this flag has no effect. On older implementations, setting this flag forces the fork() step to use vfork(2) instead of fork(2). The _GNU_SOURCE feature test macro must be defined to obtain the definition of this constant.
If this flag is set, the child process shall create a new session and become the session leader. The child process shall also become the process group leader of the new process group in the session (see setsid(2)). The _GNU_SOURCE feature test macro must be defined to obtain the definition of this constant.

Если attrp равно NULL, то выполняются действия по умолчанию, которые описаны выше по каждому флагу.

The file_actions argument specifies a sequence of file operations that are performed in the child process after the general processing described above, and before it performs the exec(3). If file_actions is NULL, then no special action is taken, and standard exec(3) semantics apply—file descriptors open before the exec remain open in the new process, except those for which the FD_CLOEXEC flag has been set. File locks remain in place.

Если file_actions не равно NULL, то в нём содержится упорядоченный набор запросов open(2), close(2) и dup2(2) на файлы. Эти запросы добавляются в file_actions с помощью posix_spawn_file_actions_addopen(3), posix_spawn_file_actions_addclose(3) и posix_spawn_file_actions_adddup2(3). Запрашиваемые операции выполняются в порядке их добавления в file_actions.

Если какая-либо обслуживающая операция завершается с ошибкой, (из-за переданных некорректных значений или по другим причинам, из-за которых обработка сигналов, планирование процесса, функции изменения ID группы процесса и операции с файловыми дескрипторами завершается с ошибкой), дочерний процесс завершается с кодом выхода 127.

Шаг exec()

После того как потомок создан (fork) и выполнены все запрошенные шаги до exec, потомок выполняет запуск запрошенного исполняемого файла.

Дочерний процесс берёт своё окружение из аргумента envp, которое рассматривается также как если бы оно передавалось в execve(2). Аргументы созданного процесса выбираются из аргумента argv, который обрабатывается также как для execve(2).

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Upon successful completion, posix_spawn() and posix_spawnp() place the PID of the child process in pid, and return 0. If there is an error during the fork() step, then no child is created, the contents of *pid are unspecified, and these functions return an error number as described below.

Даже когда эти функции выполняются без ошибок, дочерний процесс всё ещё может завершиться с ошибкой по многим причинам, касающимся инициализации до exec(). Также, может завершиться ошибкой и exec(3). Во всех этих случаях дочерний процесс завершается с кодом ошибки 127.

ОШИБКИ

The posix_spawn() and posix_spawnp() functions fail only in the case where the underlying fork(2), vfork(2) or clone(2) call fails; in these cases, these functions return an error number, which will be one of the errors described for fork(2), vfork(2) or clone(2).

Также, эти функции завершаются с ошибкой если:

Функции не поддерживаются в этой системе.

ВЕРСИИ

Функции posix_spawn() и posix_spawnp() доступны в glibc начиная с версии 2.2.

СООТВЕТСТВИЕ СТАНДАРТАМ

POSIX.1-2001, POSIX.1-2008.

ЗАМЕЧАНИЯ

Обслуживающие действия в потомке управляются объектами, на который указывает attrp (для не файловых действий) и file_actions. В описании POSIX типы данных posix_spawnattr_t и posix_spawn_file_actions_t указываются как объекты, а их элементам не даны имена. Переносимые программы должны инициализировать эти объекты с только помощью функций, определённых в POSIX (другими словами, хотя эти объекты могут быть реализованы как структуры с полями, в переносимых программах нельзя привязываться к такой реализации).

According to POSIX, it is unspecified whether fork handlers established with pthread_atfork(3) are called when posix_spawn() is invoked. Since glibc 2.24, the fork handlers are not executed in any case. On older implementations, fork handlers are called only if the child is created using fork(2).

Не существует функции «posix_fspawn» (т. е., функции типа posix_spawn(), которая вызывала бы fexecve(3) вместо execve(2)). Однако, подобное поведение можно получить указав аргумент path как один из файлов в каталоге /proc/self/fd вызывающего.

ДЕФЕКТЫ

В POSIX.1 указано, что когда в spawn-flags определён POSIX_SPAWN_SETSCHEDULER, флаг POSIX_SPAWN_SETSCHEDPARAM (если есть) игнорируется. Однако до glibc 2.14 вызов posix_spawn() завершался с ошибкой, если POSIX_SPAWN_SETSCHEDULER был указан, а POSIX_SPAWN_SETSCHEDPARAM отсутствовал.

ПРИМЕРЫ

Представленная далее программа показывает использование различных функций программного интерфейса POSIX для создания процессов. Она принимает атрибуты из командной строки, которые позволяют задать файловые действия и атрибуты объектов при создании. В остальных аргументах командной строки задаются имя исполняемого файла и аргументы командной строки для программы, исполняемой в потомке.

Здесь для исполнения в потомке указана команда date(1) и вызов posix_spawn() не использует каких-либо файловых действий и атрибутов объекта.


$ ./a.out date
PID потомка: 7634
Tue Feb  1 19:47:50 CEST 2011
Состояние потомка: завершился, состояние=0

Здесь параметром командной строки -c передаётся объект файловых действий, которые закрывают стандартный вывод в потомке. В результате этого date(1) завершается с ошибкой, когда пытается выполнить вывод данных и завершается с кодом состояния 1.


$ ./a.out -c date
PID потомка: 7636
date: write error: Bad file descriptor
Состояние потомка: завершился, состояние=1

Здесь используется параметр командной строки -s для создания объекта атрибутов, который используется для блокировки всех сигналов (блокируемых) в потомке. В результате этого попытка убить потомка сигналом по умолчанию (т. е., SIGTERM) с помощью kill(1) завершается ошибкой, так как этот сигнал заблокирован. Теперь, чтобы убить потомка, требуется сигнал SIGKILL (SIGKILL невозможно заблокировать).


$ ./a.out -s sleep 60 &
[1] 7637
$ PID потомка: 7638
$ kill 7638
$ kill -KILL 7638
$ Состояние потомка: убит по сигналу 9
[1]+  Done                    ./a.out -s sleep 60

Когда мы пытаемся выполнить в потомке несуществующую команду, exec(3) завершается с ошибкой и потомок завершается с кодом 127.


$ ./a.out xxxxx
PID потомка: 10190
Состояние потомка: завершился, состояние=127

Исходный код программы

#include <spawn.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <wait.h>
#include <errno.h>
#define errExit(msg)    do { perror(msg); \

exit(EXIT_FAILURE); } while (0) #define errExitEN(en, msg) \
do { errno = en; perror(msg); \
exit(EXIT_FAILURE); } while (0) char **environ; int main(int argc, char *argv[]) {
pid_t child_pid;
int s, opt, status;
sigset_t mask;
posix_spawnattr_t attr;
posix_spawnattr_t *attrp;
posix_spawn_file_actions_t file_actions;
posix_spawn_file_actions_t *file_actionsp;
/* разбор параметров командной строки, которые можно использовать
в потомке в качестве объекта атрибутов и файловых действий */
attrp = NULL;
file_actionsp = NULL;
while ((opt = getopt(argc, argv, "sc")) != -1) {
switch (opt) {
case 'c': /* -c: закрыть стандартный вывод в потомке */
/* создаём объект файловых действий и добавляем в него
действие «закрыть» */
s = posix_spawn_file_actions_init(&file_actions);
if (s != 0)
errExitEN(s, "posix_spawn_file_actions_init");
s = posix_spawn_file_actions_addclose(&file_actions,
STDOUT_FILENO);
if (s != 0)
errExitEN(s, "posix_spawn_file_actions_addclose");
file_actionsp = &file_actions;
break;
case 's': /* -s: блокировать все сигналы в потомке */
/* создаём объект атрибутов и добавляем в него действие
«назначения сигнальной маски» */
s = posix_spawnattr_init(&attr);
if (s != 0)
errExitEN(s, "posix_spawnattr_init");
s = posix_spawnattr_setflags(&attr, POSIX_SPAWN_SETSIGMASK);
if (s != 0)
errExitEN(s, "posix_spawnattr_setflags");
sigfillset(&mask);
s = posix_spawnattr_setsigmask(&attr, &mask);
if (s != 0)
errExitEN(s, "posix_spawnattr_setsigmask");
attrp = &attr;
break;
}
}
/* Порождение потомка. Имя исполняемой программы и аргументы
командной строки берутся из аргументов командной строки
этой программы. Окружение исполняемой программы в потомке
делается таким же как у родителя. */
s = posix_spawnp(&child_pid, argv[optind], file_actionsp, attrp,
&argv[optind], environ);
if (s != 0)
errExitEN(s, "posix_spawn");
/* уничтожаем все объекты, которые мы создали ранее */
if (attrp != NULL) {
s = posix_spawnattr_destroy(attrp);
if (s != 0)
errExitEN(s, "posix_spawnattr_destroy");
}
if (file_actionsp != NULL) {
s = posix_spawn_file_actions_destroy(file_actionsp);
if (s != 0)
errExitEN(s, "posix_spawn_file_actions_destroy");
}
printf("PID of child: %jd\n", (intmax_t) child_pid);
/* отслеживаем состояние потомка до его завершения */
do {
s = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
if (s == -1)
errExit("waitpid");
printf("Состояние потомка: ");
if (WIFEXITED(status)) {
printf("завершился, состояние=%d\n", WEXITSTATUS(status));
} else if (WIFSIGNALED(status)) {
printf("убит по сигналу %d\n", WTERMSIG(status));
} else if (WIFSTOPPED(status)) {
printf("остановлен по сигналу %d\n", WSTOPSIG(status));
} else if (WIFCONTINUED(status)) {
printf("выполняется\n");
}
} while (!WIFEXITED(status) && !WIFSIGNALED(status));
exit(EXIT_SUCCESS); }

СМ. ТАКЖЕ

close(2), dup2(2), execl(2), execlp(2), fork(2), open(2), sched_setparam(2), sched_setscheduler(2), setpgid(2), setuid(2), sigaction(2), sigprocmask(2), posix_spawn_file_actions_addclose(3), posix_spawn_file_actions_adddup2(3), posix_spawn_file_actions_addopen(3), posix_spawn_file_actions_destroy(3), posix_spawn_file_actions_init(3), posix_spawnattr_destroy(3), posix_spawnattr_getflags(3), posix_spawnattr_getpgroup(3), posix_spawnattr_getschedparam(3), posix_spawnattr_getschedpolicy(3), posix_spawnattr_getsigdefault(3), posix_spawnattr_getsigmask(3), posix_spawnattr_init(3), posix_spawnattr_setflags(3), posix_spawnattr_setpgroup(3), posix_spawnattr_setschedparam(3), posix_spawnattr_setschedpolicy(3), posix_spawnattr_setsigdefault(3), posix_spawnattr_setsigmask(3), pthread_atfork(3), <spawn.h>, Base Definitions volume of POSIX.1-2001, http://www.opengroup.org/unix/online.html

ЗАМЕЧАНИЯ

Эта страница является частью проекта Linux man-pages версии 5.10. Описание проекта, информацию об ошибках и последнюю версию этой страницы можно найти по адресу https://www.kernel.org/doc/man-pages/.

ПЕРЕВОД

Русский перевод этой страницы руководства был сделан Alexey, Azamat Hackimov <azamat.hackimov@gmail.com>, kogamatranslator49 <r.podarov@yandex.ru>, Kogan, Max Is <ismax799@gmail.com>, Yuri Kozlov <yuray@komyakino.ru> и Иван Павлов <pavia00@gmail.com>

Этот перевод является бесплатной документацией; прочитайте Стандартную общественную лицензию GNU версии 3 или более позднюю, чтобы узнать об условиях авторского права. Мы не несем НИКАКОЙ ОТВЕТСТВЕННОСТИ.

Если вы обнаружите ошибки в переводе этой страницы руководства, пожалуйста, отправьте электронное письмо на man-pages-ru-talks@lists.sourceforge.net.

1 ноября 2020 г. GNU