我有一种情况需要从一个非常大的多线程应用程序中生成一个帮助器进程,我无法完全控制它。
现在我正在使用fork()
/ exec()
。这在很多时候都有效,但在某些情况下,孩子会在exec()
发生之前奇怪地崩溃。我怀疑这是因为fork()
多线程应用程序通常被认为是一个非常糟糕的想法。
我真的非常喜欢以原子方式启动进程的方法,没有fork()
父进程:所有文件描述符都关闭,环境设置我想要的方式,CWD设置等等。这应该避免所有fork()
我的多线程父应用程序的恐怖,以及处理文件描述符继承等等posix_spawn()
应该是理想的。不幸的是,在Linux上,posix_spawn()
是使用fork()
和exec()
...
vfork()
被定义为暂停父进程,直到子进程调用exec()
。这看起来更像我想要的,但我的理解是vfork()
这些天通常被认为是历史遗物,相当于fork()
---这仍然是这样吗?
解决这个问题的最不好的方法是什么?
请注意:
这是在Linux上。涉及Java,但所有我的代码都在C中。
答案 0 :(得分:5)
应使用单个线程创建进程。如果多线程进程调用fork(),则新进程应包含调用线程的副本及其整个地址空间,可能包括互斥锁和其他资源的状态。因此,为了避免错误,子进程可能只执行异步信号安全操作,直到调用其中一个exec函数为止。可以通过pthread_atfork()函数建立fork处理程序,以便跨fork()调用维护应用程序不变量。
posix_spawn()不是最好的主意:
暂时修改多线程进程的环境也很复杂,因为所有线程必须在安全环境被更改时达成一致。但是,此成本仅由使用附加功能的posix_spawn()和posix_spawnp()调用承担。由于大量修改不是通常的情况,并且在时间关键代码中特别不可能,因此将大部分环境控制保留在posix_spawn()和posix_spawnp()之外是适当的设计。
我猜你从父资源中复制有问题。您可以使用pthread_atfork()处理程序清理它们(使用pthread,对吧?)。另一种方法是使用低级函数来创建名为clone()的进程。它几乎可以完全控制子进程应该从其父进程继承的内容。
<强> [UPDATE] 强>
摆脱问题的最简单方法可能就是改变你的分叉方案。例如,即使在程序初始化所有资源之前,您也可以创建一个新进程(fork)。即在创建所有线程之前,在main()中调用fork()。在子进程中设置信号处理程序(例如用于SIGUSR2信号)和休眠。当父需要执行一些新进程时,它会将SIGUSR2信号发送到您的子进程。当孩子捕获它时,它会调用fork / exec。
答案 1 :(得分:2)
调用fork
应该是安全的如果您将自己限制为“原始”系统调用(syscall(SYS_fork)
,syscalll(SYS_execve, ...)
等)。调用任何glibc例程,你会遇到很多麻烦。
调用vfork
根本不是你想要的东西:只有被调用vfork
的线程被挂起,而其他线程将继续运行(并且在同一地址中)空间作为vforked孩子)。这很可能会使你的生活变得复杂。
直接调用clone
是可能的,但非常棘手。我们有一个实现,允许从多线程应用程序安全分叉子进程(遗憾的是不是开源)。该代码非常非常棘手,而且非常长。