是的,您可以通过管道在不同的 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 或其他一些可执行文件
- 你应该重写这段代码并提供执行它的路径!
接下来,我们应该启动子进程。
此时我们可以覆盖 STDIN、STDOUT 和 STDERR 以获得新进程。
让我们创建一个 管道 并将孩子的 STDOUT 重定向到这个管道:
rd, wr = IO.pipe
Process.run_ruby("./test/pipetest.rb param1 param2", {:out => wr})
wr.close
请注意选项哈希:{:out => wr} - 它告诉 spawn 命令将 STDOUT 重定向到 wr 流描述符。
此外,您可以在命令行中指定参数(参见 param1 和 param2)。
请注意,我们之所以调用 wr.close,是因为我们没有在本示例的父进程中使用它。
master 将如何接收对象:
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 互斥锁)来控制对该管道的访问。