1

我需要用 Marshal 序列化 Ruby 中的对象,并通过管道将其发送到子进程。我怎样才能做到这一点?

我的代码如下所示,我的问题在评论中:

data = Marshal.dump(data)
#call sub-process
`ruby -r a_lib -e 'a_method'` #### how to send the stdout to the subprocess?

a_method 看起来像:

def a_method
  ...
  data = Marshal.load(data) #### how to load the stdout of the parent process?
  ...
end
4

2 回答 2

3

是的,您可以通过管道在不同的 ruby​​/非 ruby​​ 进程之间发送序列化对象!

让我告诉你我是怎么做的。

在这个例子中,一个主进程启动一个子进程,然后子进程使用 Marshal 序列化传输一个简单的 Hash 对象。

大师源代码:

首先,在 Process 类中声明一些帮助方法run_ruby会很有用:

#encoding: UTF-8
require 'rbconfig'
module Process
  RUBY = RbConfig::CONFIG.values_at('bindir', 'BASERUBY').join('/')
# @param [String] command
# @param [Hash] options
  def Process.run_ruby(command, options)
    spawn("#{Process::RUBY} -- #{command}", options)
  end
end

此代码只是定位 ruby​​ 可执行文件并将完整路径保存到 RUBY 常量中。

重要提示:如果您打算使用Jruby或其他一些可执行文件 - 您应该重写此代码并提供执行它的路径!

接下来,我们应该启动子进程。
此时我们可以为新进程覆盖STDINSTDOUTSTDERR
让我们创建一个管道并将孩子的STDOUT重定向到这个管道:

  rd, wr = IO.pipe
  Process.run_ruby("./test/pipetest.rb param1 param2", {:out => wr})
  wr.close

请注意选项哈希:{:out => wr} - 它告诉 spawn 命令将STDOUT重定向到wr流描述符。

此外,您可以在命令行中指定参数(请参阅param1param2 )。

请注意,我们调用wr.close是因为我们没有在此示例的父进程中使用它。

主人将如何接收对象:

message = rd.gets         # read message header with size in bytes
cb = message[5..-1].to_i  # message is in form: "data <byte_size>\n"
data = rd.read(cb)        # read message with binary object
puts "Parent read #{data.length} from #{cb} bytes:"
obj = Marshal::load(data) # unserialize object
puts obj.inspect

子源代码:

现在,序列化的对象将如何传输?
首先,孩子将序列化对象,
然后它将以以下形式发送父消息:"data <byte_size>\n"
之后它将发送序列化的对象本身。由于我们已指定将此通道用作管道,因此
子进程将向STDOUT发送对象。

#encoding: UTF-8

# obj is an example Hash object to be transmitted
obj = {
    1 => 'asd',
    'data' => 255,
    0 => 0.55
}

data = Marshal::dump(obj)             # serializing object (obj)
$stdout.puts "data #{data.length}"    # sending message header
$stdout.write data                    # sending message itself
$stdout.flush                         # Important: flush data!

在上面的代码中,子进程简单地输出一个序列化对象并终止。
但是,当然,您可以编写更复杂的行为。
例如,我启动了许多子进程,每个子进程都与STDOUT的父进程共享一个相同的管道。为了避免两个孩子同时写入管道的问题,我必须使用系统级互斥锁(不是 Ruby 互斥锁)来控制对该管道的访问。

于 2012-11-06T19:32:26.313 回答
1

你可以使用IO::pipe方法。

我认为您选择的不是创建子进程的最佳方式。Backticks 在幕后也是如此fork,而在幕后也是如此。这意味着您的命令:execrubyforkexec

`ruby -r a_lib -e 'a_method'`

执行以下操作:fork 当前进程,将其转换为 shell 进程,fork shell 进程,将其转换为 ruby​​ 进程。

我建议使用fork方法:

data = Marshal.dump(data)

reader, writer = IO.pipe
reader.close # parent process will be on the writing side of the pipe
writer.puts data

#call sub-process
fork do
  writer.close # child process can only read from the pipe
  data = reader.gets
  # whatever needs to be done with data
end
于 2012-08-10T11:10:28.863 回答