从多线程应用程序生成进程

问题描述 投票:0回答:3

我遇到了一种情况,我需要从一个非常大的多线程应用程序中生成一个辅助进程,而我无法完全控制该应用程序。

现在我正在使用

fork()
/
exec()
。这在很多时候都有效,但在某些情况下,孩子会在
exec()
发生之前奇怪地崩溃。我怀疑这是因为
fork()
多线程应用程序通常被认为是一个非常糟糕的主意。

我真的非常非常喜欢一种原子方式启动进程的方法,而不用

fork()
处理父进程:关闭所有文件描述符,按照我想要的方式设置环境,设置 CWD 等。这应该避免
 的所有恐怖fork()
我的多线程父应用程序,并处理文件描述符继承等。
posix_spawn()
应该是理想的选择。不幸的是,在 Linux 上,
posix_spawn()
是使用
fork()
exec()
...

实现的

vfork()
定义为挂起父进程,直到子进程调用
exec()
。这看起来更像是我想要的,但我的理解是,现在
vfork()
通常被认为是历史遗迹,相当于
fork()
--- 仍然是这样吗?

处理这个问题最不糟糕的方法是什么?

请注意:

  • 我无法在任何线程启动之前生成进程(因为此时我无法运行代码)
  • 由于外部要求,我无法重新设计我的应用程序以不需要帮助程序
  • 在生成辅助进程之前我无法挂起所有线程,因为它们不属于我

这是在 Linux 上。涉及 Java,但所有我的代码都在 C 中。

c linux unix process fork
3个回答
5
投票

如果您仅使用异步信号安全操作,则分叉多线程应用程序被认为是安全的。 POSIX

进程应使用单线程创建。如果多线程进程调用 fork(),新进程应包含调用线程及其整个地址空间的副本,可能包括互斥体和其他资源的状态。因此,为了避免错误,子进程只能执行异步信号安全操作,直到调用 exec 函数之一为止。 Fork 处理程序可以通过 pthread_atfork() 函数建立,以便在 fork() 调用之间保持应用程序不变性。

posix_spawn() 不是最好的主意:

临时修改多线程进程的环境也很复杂,因为所有线程必须同意何时更改环境是安全的。但是,此成本仅由使用附加功能的 posix_spawn() 和 posix_spawnp() 的调用承担。由于广泛的修改并不常见,并且在时间关键的代码中尤其不可能,因此将大部分环境控制保留在 posix_spawn() 和 posix_spawnp() 之外是适当的设计。

(参见 man posix_spawn

我猜您在从父资源复制时遇到问题。您可以使用 pthread_atfork() 处理程序清理它们(您使用 pthread,对吧?)。另一种方法是使用称为 clone() 的低级函数来创建进程。它使您几乎可以完全控制子进程应该从其父进程继承什么。

[更新]

解决这个问题最简单的方法可能就是改变你的分叉方案。例如,您甚至可以在程序初始化所有资源之前创建一个新进程(fork)。 IE。在创建所有线程之前,在 main() 中调用 fork() 。在子进程中设置信号处理程序(例如 SIGUSR2 信号)并睡眠。当父进程需要执行某个新进程时,它会向子进程发送 SIGUSR2 信号。当子进程捕获它时,它会调用 fork/exec。


2
投票

如果

您将自己限制为“原始”系统调用(
forksyscall(SYS_fork)
等),那么调用 
syscalll(SYS_execve, ...)
 应该是安全的。调用任何 glibc 例程,你都会遇到很多麻烦。

调用

vfork

 根本不是你想要的:只有调用 
vfork 的线程被挂起,其他线程将继续运行(并且与 vforked 子级在同一地址空间中)。这很可能会让你的生活变得复杂。
直接调用
clone

是可能的,但非常棘手。我们有一个实现,允许从多线程应用程序安全分叉子进程(不幸的是不是开源的)。该代码

非常

棘手,而且出人意料的长。

好吧,考虑一下,在Linux中,fork可以在线程内调用。但是,您必须记住一些重要的注意事项。当在多线程程序中调用fork时,只有调用线程在子进程中被复制,而所有其他线程都不会被复制。如果其他线程持有锁或正在进行关键操作,这可能会导致问题。 posix_spawn 的设计更加安全,因为它避免了与 fork 相关的陷阱。

0
投票

© www.soinside.com 2019 - 2024. All rights reserved.