使用Process.spawn替代Process.fork

Bra*_*rth 16 ruby windows fork ruby-on-rails

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

在使用第三方宝石时,一次又一次出现的问题是Windows上缺少fork().这最近阻碍了我使用几乎任何运行gem(如这些)的分布式测试的能力,因为它们依赖于fork.

关于StackOverflow的一些旧问题试图找到解决同一问题的方法,但要么是在将Process.spawn添加到ruby之前,要么是因为其他原因而被迫使用旧版Ruby的人.

建议的解决方案之一是使用Cygwin来获得fork()支持,这对此毫无疑问 - 我认为在此之前我更愿意完全切换到Linux.

另一个提议的解决方案是使用win32-process gem来获得fork()支持.从最新版本(0.7.0)中删除了fork支持,使用下一个最旧的版本(0.6.6),它支持fork(sort-of)支持fork似乎不起作用,至少对于运行任何分布式测试我尝试的宝石(Spork,Parallel tests,Hydra,Specjour,几乎所有这些).有趣的是,gem的作者在自述文件中提到Process.spawn是Process.fork的可接受的解决方法.

我已经看到很多信息要么暗示,要么直接说明spawn可以在Windows上使用Ruby 1.9作为fork的替代品.我花了相当多的时间玩这个,基本上尝试用几个引用的宝石中的Process.spawn替换Process.fork,但没有成功.在我看来,也许这种行为是相似的,但并不完全相同.例如,不清楚spawn是否实际以fork的相同方式复制整个进程,或者只是使用提供的参数创建一个新进程.还不清楚spawn方法是否接受另一个ruby方法作为参数,或者只接受系统命令.文档似乎暗示它只是一个命令,但一种方法似乎有效(排序),但我可能做错了.我认为,对于某些事情,fork只是用来创建一个"廉价线程",在以前的ruby版本中不支持线程.但是,似乎这些分布式测试宝石可能合法地依赖fork()的全部功能,以便维护项目状态,并且不会为每个测试加载整个ruby环境.这有点超出了我正常的编程职责和经验,因此我可能会做出一些不正确的假设.

所以,我的问题是,在所有情况下,可以相对简单地使用Process.spawn来实现与Process.fork相同的结果吗?我开始怀疑不是,但如果是这样,有人可以发一个如何进行转型的例子吗?

Lin*_*ios 16

编辑:有一个常见的用例fork()可以替换为spawn()- the fork()- exec()combo.许多较旧(和现代)的UNIX应用程序,当它们想要生成另一个进程时,将首先进行分叉,然后进行exec调用(exec将当前进程替换为另一个进程).这实际上并不需要fork(),这就是它可以替换的原因spawn().所以这:

if(!fork())
  exec("dir")
end
Run Code Online (Sandbox Code Playgroud)

可以替换为:

Process.spawn("dir")
Run Code Online (Sandbox Code Playgroud)

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


编辑:win32-process的实现fork()不起作用的原因是(据我从文档中可以看出),它基本上 spawn(),这根本不是fork().


不,我不认为可以做到.您看,Process.spawn使用默认空白状态和本机代码创建新进程.所以,虽然我可以这样做Process.spawn('dir')将启动一个新的,空白的进程在运行dir,它不会复制任何当前进程的状态.它与您的程序的唯一连接是父子连接.

你看,fork()是一个非常低级别的电话.例如,在Linux上,fork()基本上是这样做的:首先,创建一个具有完全克隆的寄存器状态的新进程.然后,Linux对所有父进程的页面执行写时复制引用.Linux然后克隆一些其他进程标志.显然,所有这些操作只能由内核完成,并且Windows内核没有这样做的工具(并且不能修补它们).

从技术上讲,只有本机程序需要操作系统才能获得某种类似的fork()支持.任何代码层都需要它上面的层的合作来做类似的事情fork().因此,虽然本机C代码需要内核与fork的配合,但Ruby理论上只需要解释器的合作来做分叉.但是,Ruby解释器没有快照/恢复功能,这必须实现fork.因此,普通的Ruby fork是通过分析解释器本身而不是Ruby程序来实现的.

那么,如果您可以修补Ruby解释器以添加停止/启动和快照/恢复功能,那么您可以这样做,但除此之外?我不这么认为.

那么你有什么选择呢?这是我能想到的:

  • 修补Ruby解释器
  • 修补fork()用于使用线程或生成的代码
  • 获取UNIX(我建议这个)
  • 使用Cygwin

编辑1:我不建议使用Cygwin的fork,因为它涉及特殊的Cygwin进程表,没有 copy-on-write,这使得效率非常低.此外,它涉及大量的来回跳跃和大量的复制.尽可能避免使用它.此外,由于Windows没有提供复制地址空间的工具,因此很可能会出现故障,并且很多时候都会出现问题(参见此处).