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()

Функция posix_spawn() начинает работу с вызова fork(2) или, возможно, vfork(2) (смотрите ниже).

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

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

Дочерний процесс создаётся с помощью vfork(2), а не fork(2) в любом из следующих случаев:

  • элемент 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(): служебные действия

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

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 группы процесса родителя.

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

Аргумент file_actions задаёт последовательность файловых операций, которые выполняются в дочернем процессе после общей обработки, описанной выше, и перед выполнением exec(3). Если file_actions равно NULL, то никаких специальных действий не производится и выполняются стандартные действия exec(3) — файловые дескрипторы, открытые до выполнения exec, остаются открытыми и в новом процессе, за исключением тех, у которых установлен флаг FD_CLOEXEC. Файловые блокировки остаются как были.

Если 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).

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

При успешном завершении posix_spawn() и posix_spawnp() помещают PID дочернего процесса в pid и возвращают 0. Если перед или во время fork(2) возникла ошибка, то потомок не создаётся, содержимое *pid неопределенно и функции возвращают номер ошибки (описано далее).

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

ОШИБКИ

Функции posix_spawn() и posix_spawnp() завершаются с ошибкой, только из-за ошибок в используемых вызовах fork(2) и vfork(2); в этих случаях эти функции возвращают номер ошибки, который может быть одним из описанных в fork(2) или vfork(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 (другими словами, хотя эти объекты могут быть реализованы как структуры с полями, в переносимых программах нельзя привязываться к такой реализации).

В POSIX не определено вызывать ли обработчики fork, установленные с помощью pthread_atfork(3), при вызове posix_spawn(). В glibc обработчики fork вызываются только, если потомок создан с помощью 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 <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 потомка: %ld\n", (long) 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

2019-03-06 GNU