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_spawn() и posix_spawnp() является способ, которым в них указывается исполняемый дочерним процессом файл. В posix_spawn() исполняемый файл задаётся в виде пути (которое может быть абсолютным или относительным). В posix_spawnp() исполняемый файл задаётся в виде имени файла; система ищет этот файл в списке каталогов, указанных в PATH (также, как это делает execvp(3)). Кроме данного отличия далее на этой странице всё описание posix_spawn() также относится и к posix_spawn(). Остальные аргументы функций:

*   Аргумент 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:

POSIX_SPAWN_SETSIGMASK
    Назначить маску сигналов равной набору сигналов, определённой в атрибуте spawn-sigmask объекта, на который указывает attrp. Если не установлен флаг POSIX_SPAWN_SETSIGMASK, то потомок наследует маску сигналов родителя.

POSIX_SPAWN_SETSIGDEF
    Сбрасывает обработчики всех сигналов в наборе, заданном в атрибуте spawn-sigdefault объекта, на который указывает attrp, в значения по умолчанию. О том, что происходит с обработчиками сигналов не указанных в атрибуте spawn-sigdefault или когда не указан POSIX_SPAWN_SETSIGDEF, смотрите execve(2).

POSIX_SPAWN_SETSCHEDPARAM
    Если этот флаг установлен, а POSIX_SPAWN_SETSCHEDULER нет, то изменяет параметры планирования на значения, указанные в атрибуте spawn-schedparam объекта, на который указывает attrp.

POSIX_SPAWN_SETSCHEDULER
    Назначает алгоритм планирования и параметры потомка:

    *   Алгоритму планирования присваивается значение, указанное в атрибуте spawn-schedpolicy объекта, на который указывает attrp.

    *   Параметрам планирования присваивается значение, указанное в атрибуте spawn-schedparam объекта, на который указывает attrp (но смотрите ДЕФЕКТЫ).

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

POSIX_SPAWN_RESETIDS
    Если этот флаг установлен, то сбрасываются эффективный UID и GID в реальный UID и GID родительского процесса. Если флаг не установлен, то потомок сохраняет эффективный UID и GID родителя. В любом случае, если биты прав set-user-ID и set-group-ID включены на исполняемом файле, то это заменяет значения эффективного UID и GID (смотрите execve(2)).

POSIX_SPAWN_SETPGROUP
    Назначает группе процесса значение, указанное в атрибуте 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).

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

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

ВЕРСИИ
Функции 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