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().
Остальные аргументы функций:
Далее функции описаны в виде трёх ступенчатого процесса: шаг fork(), шаг перед exec() (выполняется в потомке) и шаг exec() (выполняется в потомке).
Функция posix_spawn() начинает работу с вызова fork(2) или, возможно, vfork(2) (смотрите ниже).
PID нового дочернего процесса помещается в *pid. После этого функция posix_spawn() возвращает управление родительскому процессу.
Соответственно, родитель может использовать один из системных вызовов, описанных в wait(2), для проверки состояния дочернего процесса. Если потомок завершится с ошибкой в любом из служебных шагов, описанных далее, или возникнет ошибка при выполнении желаемого файла, то он завершит работу с кодом состояния 127.
Дочерний процесс создаётся с помощью vfork(2), а не fork(2) в любом из следующих случаев:
Иначе говоря, vfork(2) используется, если это запросил вызывающий или не нужна очистка в потомке перед выполнением exec(3) запрашиваемого файла.
Между fork(2) и exec(3) дочерний процесс может выполнить набор служебных действий. Функции posix_spawn() и posix_spawnp() поддерживают маленький, хорошо спроектированный набор системных задач, которые дочерний процесс может выполнить перед запуском исполняемого файла. Эти операции управляются объектом атрибутов, на который указывает attrp и объект файловых действий, на который указывает file_actions. В потомке обработка выполняются в следующей последовательности:
Все атрибуты процесса-потомка, отличные от атрибутов в объекте, на который указывает attrp и файловые действия в объекте, на который указывает file_actions, будут изменены как если бы потомок создавался с помощью fork(2) и выполнял программу с помощью execve(2).
Действия атрибутов процесса определяются атрибутами объекта, на который указывает attrp. Атрибут spawn-flags (устанавливается с помощью posix_spawnattr_setflags(3)) управляет общими действиями, а остальные атрибуты объекта хранят значения, которые будут использованы в этих действиях.
Влияние флагов, которые могут быть указаны в spawn-flags:
Если не указаны флаги POSIX_SPAWN_SETSCHEDPARAM и POSIX_SPAWN_SETSCHEDPOLICY, то потомок наследует соответствующие атрибуты планирования от родителя.
Если 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.
После того как потомок создан (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 |