はい、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} - STDOUTをwrストリーム記述子にリダイレクトするように spawn コマンドに指示します。
また、コマンド ラインでparam ( param1およびparam2を参照) を指定することもできます。
この例の親プロセスでは使用しないため、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!
上記のコードでは、子プロセスは単純に 1 つのシリアル化されたオブジェクトを出力して終了します。
もちろん、もっと複雑な動作をプログラムすることもできます。
たとえば、多くの子プロセスを開始し、それぞれがSTDOUTで親プロセスへの同じパイプを共有しています。2 人の子が同時にパイプに書き込みを行うという問題を回避するには、システム レベルのミューテックス(Ruby ミューテックスではなく) を使用して、このパイプへのアクセスを制御する必要があります。