我很想知道是否可以使用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)
有什么想法吗?
答案 0 :(得分:9)
正如其他人在评论中指出的那样,posix_spawn
被允许创建一个子进程,由于执行失败或其他post-fork失败而立即死亡;调用应用程序需要为此做好准备。但当然最好不要这样做。
an answer I wrote在What 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的解决方案似乎是使用clone
和CLONE_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规范并编写代码(而且我不是一个内核黑客),但我仍然认为这个概念有腿,我很乐意与之合作有人完成它。