POSIX_SPAWN - Linux手册页

时间:2019-08-20 18:00:59  来源:igfitidea点击:

Linux程序员手册 第3部分
更新日期: 2020-04-11

名称

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_spawnp()仅在上述方面有所不同。

这两个函数的其余参数如下:

*
pid参数指向一个缓冲区,该缓冲区用于返回新的子进程的进程ID。
*
file_actions参数指向一个生成文件操作对象,该对象指定在fork(2)和exec(3)步骤之间的子级中执行的与文件相关的操作。使用posix_spawn_file_actions_init(3)和posix_spawn_file_actions _ *()函数在posix_spawn()调用之前初始化并填充此对象。
*
attrp参数指向一个属性对象,该对象指定创建的子进程的各种属性。使用posix_spawnattr_init(3)和posix_spawnattr _ *()函数在posix_spawn()调用之前初始化并填充此对象。
*
execve(2)一样,argv和envp参数指定在子进程中执行的程序的参数列表和环境。

下面,按照三步过程来描述函数:fork()步骤,pre-exec()步骤(在子代中执行)和exec()步骤(在子代中执行)。

fork() step

从glibc 2.24开始,posix_spawn()函数通过调用带有CLONE_VM和CLONE_VFORK标志的clone(2)开始。较旧的实现使用fork(2)或可能的vfork(2)(请参见下文)。

新的子进程的PID放在* pid中。然后posix_spawn()函数将控制权返回给父进程。

随后,父级可以使用wait(2)中描述的系统调用之一来检查子进程的状态。如果孩子在以下所述的任何内务处理步骤中失败,或未能执行所需的文件,则其退出状态为127。

在glibc 2.24之前,如果满足以下任一条件,则使用vfork(2)而不是fork(2)创建子进程:

*
attrp指向的属性对象的spawn-flags元素包含特定于GNU的标志POSIX_SPAWN_USEVFORK;要么
*
file_actions为NULL,attrp指向的属性对象的spawn-flags元素不包含POSIX_SPAWN_SETSIGMASKPOSIX_SPAWN_SETSIGDEFPOSIX_SPAWN_SETSCHEDPARAMPOSIX_SPAWN_SETSCHEDULER,POSIX_SPAWNN_SETPGS,或

换句话说,如果调用者请求vfork(2),或者在exec(3)所请求的文件之前子级中不希望进行清理,则使用vfork(2)。

pre-exec() step: housekeeping

在fork()和exec()步骤之间,子进程可能需要执行一组内务操作。 posix_spawn()和posix_spawnp()函数支持一整套定义明确的系统任务,子进程在执行可执行文件之前可以完成这些任务。这些操作由attrp指向的属性对象和file_actions指向的文件操作对象控制。在子进程中,处理按以下顺序进行:

1.
流程属性操作:信号掩码,信号默认处理程序,调度算法和参数,流程组以及有效的用户ID和组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)设置)控制发生的一般操作,并且对象中的其他属性指定在这些操作期间要使用的值。

可以在生成标志中指定的标志的效果如下:

POSIX_SPAWN_SETSIGMASK
将信号掩码设置为attrp指向的对象的spawn-sigmask属性中指定的信号集。如果未设置POSIX_SPAWN_SETSIGMASK标志,则子级继承父级的信号掩码。
POSIX_SPAWN_SETSIGDEF
将attrp指向的对象的spawn-sigdefault属性中指定的集合中的所有信号的设置重置为默认值。有关spawn-sigdefault属性中未指定的信号处理的处理,或未指定POSIX_SPAWN_SETSIGDEF时的处理,请参见execve(2)。
POSIX_SPAWN_SETSCHEDPARAM
如果设置了此标志,并且未设置POSIX_SPAWN_SETSCHEDULER标志,则将调度参数设置为attrp指向的对象的spawn-schedparam属性中指定的参数。
POSIX_SPAWN_SETSCHEDULER
Set the scheduling policy algorithm and parameters of the child, as follows:
*
调度策略设置为attrp指向的对象的spawn-schedpolicy属性中指定的值。
*
调度参数设置为attrp指向的对象的spawn-schedparam属性中指定的值(但请参见BUGS)。

如果未指定POSIX_SPAWN_SETSCHEDPARAM和POSIX_SPAWN_SETSCHEDPOLICY标志,则子级将从父级继承相应的调度属性。

POSIX_SPAWN_RESETIDS
如果设置了此标志,则将有效UID和GID重置为父进程的实际UID和GID。如果未设置此标志,则子级将保留父级的有效UID和GID。在这两种情况下,如果在可执行文件上启用了"设置用户ID"和"设置组ID"权限位,它们的效果将覆盖有效UID和GID的设置(se execve(2))。
POSIX_SPAWN_SETPGROUP
将进程组设置为attrp指向的对象的spawn-pgroup属性中指定的值。如果spawn-pgroup属性的值为0,则使子进程的进程ID与进程的ID相同。如果未设置POSIX_SPAWN_SETPGROUP标志,则子级将继承父级的进程组ID。
POSIX_SPAWN_USEVFORK
从glibc 2.24开始,此标志无效。在较旧的实现中,设置此标志将强制fork()步骤使用vfork(2)而不是fork(2)。必须定义_GNU_SOURCE功能测试宏才能获得此常量的定义。
POSIX_SPAWN_SETSID(since glibc 2.26)
如果设置了此标志,则子进程将创建一个新会话并成为会话领导者。子进程也将成为会话中新进程组的进程组负责人(请参见setsid(2))。必须定义_GNU_SOURCE功能测试宏才能获得此常量的定义。

如果attrp为NULL,则适用上述每个标志的默认行为。

file_actions参数指定在上述常规处理之后,在执行exec(3)之前在子进程中执行的文件操作序列。如果file_actions为NULL,则不执行任何特殊操作,并且将应用标准exec(3)语义-在新进程中exec保持打开之前打开文件描述符,但已设置FD_CLOEXEC标志的描述符除外。文件锁保持不变。

如果file_actions不为NULL,则它包含对open(2),close(2)和dup2(2)文件的有序请求集。这些请求通过posix_spawn_file_actions_addopen(3),posix_spawn_file_actions_addclose(3)和posix_spawn_file_actions_adddup2(3)添加到file_actions中。所请求的操作按照它们添加到file_actions中的顺序执行。

如果任何管家操作失败(由于传递了虚假值或由于信号处理,进程调度,进程组ID函数和文件描述符操作可能失败的其他原因),则子进程退出,退出值为127。

exec() step

子级成功分叉并执行了所有请求的执行前步骤后,子级将运行请求的可执行文件。

子进程从envp参数获取环境,该环境被解释为好像已传递给execve(2)。创建的进程的参数来自argv参数,该参数与execve(2)一样进行处理。

返回值

成功完成后,posix_spawn()和posix_spawnp()将子进程的PID放入pid中,并返回0。如果fork()步骤期间发生错误,则不会创建任何子进程,因此* pid的内容未指定,这些函数将返回一个错误号,如下所述。

即使这些函数返回成功状态,子进程仍可能由于与其pre-exec()初始化有关的多种原因而失败。此外,exec(3)可能会失败。在所有这些情况下,子进程将退出,退出值为127。

错误说明

仅在基础fork(2),vfork(2)或clone(2)调用失败的情况下,posix_spawn()和posix_spawnp()函数才会失败。在这些情况下,这些函数将返回错误号,该错误号将是为fork(2),vfork(2)或clone(2)描述的错误之一。

此外,如果出现以下情况,这些功能将失败:

ENOSYS
该系统不支持该功能。

版本

从glibc 2.2开始,posix_spawn()和posix_spawnp()函数可用。

遵循规范

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

备注

子级中的管家活动由attrp指向的对象(用于非文件操作)和file_actions控制。在POSIX中,posix_spawnattr_t和posix_spawn_file_actions_t数据类型称为对象,并且其元素未按名称指定。可移植程序应仅使用POSIX指定的函数来初始化这些对象。 (换句话说,尽管这些对象可以实现为包含字段的结构,但可移植程序必须避免依赖于此类实现细节。)

根据POSIX,尚不确定在调用posix_spawn()时是否调用通过pthread_atfork(3)建立的fork处理程序。从glibc 2.24开始,在任何情况下都不会执行fork处理程序。在较旧的实现中,仅当使用fork(2)创建子代时才调用fork处理程序。

没有" posix_fspawn"函数(即,将posix_spawn()用作fexecve(3)的函数是将execve(2)的函数)。但是,可以通过将path参数指定为调用方的/ proc / self / fd目录中的文件之一来获得此功能。

BUGS

POSIX.1表示,当在生成标志中指定POSIX_SPAWN_SETSCHEDULER时,将忽略POSIX_SPAWN_SETSCHEDPARAM。但是,在glibc 2.14之前,如果在未指定POSIX_SPAWN_SETSCHEDULER的情况下也未指定POSIX_SPAWN_SETSCHEDPARAM,则对posix_spawn()的调用失败,并显示错误。

示例

下面的程序演示了POSIX spawn API中各种功能的用法。该程序接受可用于创建文件操作和属性对象的命令行属性。其余命令行参数用作在子级中执行的程序的可执行文件名称和命令行参数。

在第一次运行中,date(1)命令在子级中执行,而posix_spawn()调用不使用文件操作或属性对象。

$ ./a.out date
PID of child: 7634
Tue Feb  1 19:47:50 CEST 2011
Child status: exited, status=0

在下一次运行中,-c命令行选项用于创建文件操作对象,该对象将关闭子级中的标准输出。因此,date(1)在尝试执行输出时失败,并以状态1退出。

$ ./a.out -c date
PID of child: 7636
date: write error: Bad file descriptor
Child status: exited, status=1

在下一次运行中,-s命令行选项用于创建一个属性对象,该对象指定应阻止子级中的所有(可阻止的)信号。因此,尝试使用kill(1)发送的默认信号(即SIGTERM)杀死孩子,因为该信号被阻止。因此,要杀死孩子,SIGKILL是必需的(不能阻止SIGKILL)。

$ ./a.out -s sleep 60 &
[1] 7637
$ PID of child: 7638

$ kill 7638
$ kill -KILL 7638
$ Child status: killed by signal 9
[1]+  Done                    ./a.out -s sleep 60

当我们尝试在子代中执行不存在的命令时,exec(3)失败,子代退出,状态为127。

$ ./a.out xxxxx
PID of child: 10190
Child status: exited, status=127

Program source

#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;

    /* Parse command-line options, which can be used to specify an
       attributes object and file actions object for the child. */

    attrp = NULL;
    file_actionsp = NULL;

    while ((opt = getopt(argc, argv, "sc")) != -1) {
        switch (opt) {
        case aqcaq:       /* -c: close standard output in child */

            /* Create a file actions object and add a "close"
               action to it */

            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 aqsaq:       /* -s: block all signals in child */

            /* Create an attributes object and add a "set signal mask"
               action to it */

            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;
        }
    }

    /* Spawn the child. The name of the program to execute and the
       command-line arguments are taken from the command-line arguments
       of this program. The environment of the program execed in the
       child is made the same as the parentaqs environment. */

    s = posix_spawnp(&child_pid, argv[optind], file_actionsp, attrp,
                     &argv[optind], environ);
    if (s != 0)
        errExitEN(s, "posix_spawn");

    /* Destroy any objects that we created earlier */

    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: %ld\n", (long) child_pid);

    /* Monitor status of the child until it terminates */

    do {
        s = waitpid(child_pid, &status, WUNTRACED | WCONTINUED);
        if (s == -1)
            errExit("waitpid");

        printf("Child status: ");
        if (WIFEXITED(status)) {
            printf("exited, status=%d\n", WEXITSTATUS(status));
        } else if (WIFSIGNALED(status)) {
            printf("killed by signal %d\n", WTERMSIG(status));
        } else if (WIFSTOPPED(status)) {
            printf("stopped by signal %d\n", WSTOPSIG(status));
        } else if (WIFCONTINUED(status)) {
            printf("continued\n");
        }
    } while (!WIFEXITED(status) && !WIFSIGNALED(status));

    exit(EXIT_SUCCESS);
}

出版信息

这个页面是Linux手册页项目5.08版的一部分。有关项目的说明、有关报告错误的信息以及此页面的最新版本,请访问https://www.kernel.org/doc/man-pages/