在Linux上实现posix_spawn

时间:2014-08-14 14:37:25

标签: c linux posix

我很想知道是否可以使用vfork + exec的组合在Linux中实施posix_spawn。以一种非常简单的方式(省略了大多数可选参数),这可能看起来或多或少像这样:

int my_posix_spawn(pid_t *ppid, char **argv, char **env)
{
    pid_t pid;

    pid = vfork();
    if (pid == -1)
        return errno;

    if (pid == 0)
    {
        /* Child */
        execve(argv[0], argv, env);

        /* If we got here, execve failed. How to communicate this to
         * the parent? */
        _exit(-1);
    }

    /* Parent */
    if (ppid != NULL)
        *ppid = pid;

    return 0;
}

但是我想知道如何应对vfork成功的情况(因此创建子进程)但exec调用失败。似乎没有办法将此信息传达给父母,只能看到它显然可以成功创建子进程(因为它会获得有效的pid)

有什么想法吗?

2 个答案:

答案 0 :(得分:9)

正如其他人在评论中指出的那样,posix_spawn被允许创建一个子进程,由于执行失败或其他post-fork失败而立即死亡;调用应用程序需要为此做好准备。但当然最好不要这样做。

an answer I wroteWhat can cause exec to fail? What happens next?上就an article on ewontfix.com描述了向父母传达执行失败的一般程序。

不幸的是,由于其令人讨厌的返回两次语义,您需要执行的某些操作在vfork之后不合法。我曾在http://git.musl-libc.org/cgit/musl/tree/src/process/posix_spawn.c?id=v1.1.4中介绍过这个话题。使posix_spawn避免重复VM的解决方案似乎是使用cloneCLONE_VM(可能还有CLONE_VFORK)来获得共享内存的新进程但不会t在同一堆栈上运行。但是,这仍然需要非常小心,以避免对可能修改父级使用的内存的libc函数进行任何调用。我目前的实施是:

{{3}}

你可以看到它相当复杂。阅读git历史可能会提供一些有关设计决策的信息。

答案 1 :(得分:1)

我认为使用当前的系统调用没有任何好办法。您已正确识别出最大的问题 - 在vfork之后没有任何可靠的报告失败的方法。其他问题包括设置子状态的竞争条件,以及Linux对拾取closefrom缺乏兴趣。

几年前,我绘制了一个新的系统级API来解决这个问题:关键的添加是一个系统调用,我称之为egg(),它创建一个进程而不给它一个地址空间,并继承没有来自父母的州。显然,egg程序不能执行代码;但你可以(用一大堆新的系统调用)设置它的所有内核端状态,然后(用另一个系统调用,hatch())加载一个可执行文件并设置它去。至关重要的是,所有新系统调用都报告父级中的失败。例如,有一个dup_into(pid, to_fd, from_fd)调用将父文件描述符from_fd复制到egg-state进程pid的文件描述符to_fd;如果失败,父母将获得失败代码。

我从来没有时间把所有这些都融入一个连贯的API规范并编写代码(而且我不是一个内核黑客),但我仍然认为这个概念有腿,我很乐意与之合作有人完成它。