使用Process.spawn作为Process.fork的替代品

我的开发环境是运行ruby 1.9.3p125(RubyInstaller)和rails 3.2.8的Windows机器。

在使用第三方gem时,一次又一次出现的问题是缺lessWindows上的fork()。 这最近阻碍了我使用几乎任何运行gem的分布式testing(就像这些 ),因为它们依赖于fork。

一些StackOverflow的老问题试图find解决这个问题的方法,但是在将Process.spawn添加到ruby之前,或者是由于其他原因而被迫使用旧版本的Ruby的人。

提议的解决scheme之一是使用Cygwin获得fork()支持,这是完全没有问题的 – 我想我宁愿在这之前完全切换到Linux。

另一个build议的解决scheme是使用win32-process gem来获得fork()支持。 从最新的版本(0.7.0)中删除叉支持,并使用下一个最早的版本(0.6.6),它支持(叉)支持叉似乎没有工作,至less运行任何分布式testing我尝试过的gem(Spork,并行testing,Hydra,Specjour,几乎所有的)。 有趣的是,gem的作者在自述中提到Process.spawn是Process.fork的一个可以接受的解决方法。

我已经看到很多信息,或者暗示,或者直接声明 ,在Windows上,使用Ruby 1.9,派生可以用作fork的替代品。 我已经花了相当多的时间玩这个,基本上试图用几个引用的gem中的Process.spawn代替Process.fork,但没有成功。 在我看来,也许行为是相似的,但不完全一样。 例如,不清楚spawn实际上是以fork的相同方式复制整个过程,还是仅仅使用提供的参数创build一个新的过程。 也不清楚spawn方法是否接受另一个ruby方法作为参数,或者只接受一个系统命令。 文件似乎意味着它只是一个命令,但似乎是一种方法(sorting),但我可能做的不正确。 我认为对于某些事情来说,fork只是用来创build一个“便宜的线程”,在以前的ruby版本中不支持线程。 然而,似乎这些分布式testing的gem可能合法地依赖于fork()的全部function,以保持项目状态,并且不会为每个testing加载整个ruby环境。 这是在我的正常编程职责和经验之外,所以我可能会做出一些不正确的假设。

所以,我的问题是,在所有情况下,Process.spawn是否可以相对简单地使用以达到与Process.fork相同的结果? 我开始怀疑不是,但是如果是这样的话,有人可以举一个例子来说明如何去做这个转变吗?

编辑 :有一个可以用spawn()fork()exec()组合replacefork()常见用例。 许多旧的(和现代的)UNIX应用程序,当他们想要产生另一个进程时,将首先分叉,然后进行一次exec调用( exec将当前进程replace为另一个进程)。 这实际上并不需要fork() ,这就是为什么可以用spawn()replace它的原因。 所以这:

 if(!fork()) exec("dir") end 

可以replace为:

 Process.spawn("dir") 

如果任何gem像这样使用fork() ,修复很容易。 否则,这几乎是不可能的。


编辑 :为什么win32进程的实现fork()不起作用的是(据我所知,从文档),它基本上 spawn() ,这是不是fork()根本。


不,我不认为这是可以做到的。 您会看到, Process.spawn会使用默认的空白状态和本地代码创build一个新的进程。 所以,虽然我可以像Process.spawn('dir')启动一个新的运行dir 空白进程,但它不会克隆当前进程的任何状态。 这只是连接到你的程序是父 – 子连接。

你看, fork()是一个非常低层次的调用。 例如,在Linux上, fork()基本上是这样做的:首先,创build一个具有完全克隆寄存器状态的新进程。 然后,Linux对所有父进程的页面进行copy-on-write引用。 Linux然后克隆一些其他进程标志。 显然,所有这些操作只能由内核来完成,Windows内核没有这样的function(也不能修补)。

从技术上讲,只有本地程序需要某种类似fork()的支持。 任何一层代码都需要它上面的层的配合来做类似fork()事情。 所以当本地C代码需要内核与fork的配合时,理论上Ruby只需要解释器的配合做一个fork。 但是,Ruby解释器不具有快照/恢复function,这必然会实现一个分叉。 正因为如此,普通的Ruby分支是通过分解解释器而不是Ruby程序来实现的。

所以,如果你可以修补Ruby解释器来添加一个停止/启动和快照/恢复function,你可以这样做,但是否则呢? 我不这么认为。

那么你有什么select? 这是我能想到的:

  • 修补Ruby解释器
  • 修补使用fork()可能使用线程或产生的代码
  • 得到一个UNIX(我build议这个)
  • 使用Cygwin

编辑1:我不会build议使用Cygwin的fork,因为它涉及特殊的Cygwin进程表, 没有写时拷贝,这使得它非常低效。 而且,它涉及到很多来回跳动和大量的复制。 如果可能的话避免它。 另外,由于Windows没有提供复制地址空间的function,叉子很可能会失败,并且会花费相当多的时间(请参见此处 )。