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やその他の実行可能ファイルを使用する場合は、このコードを書き直して、実行するためのパスを指定する必要があります。

次に、子プロセスを開始する必要があります。
この時点で、新しいプロセスのSTDINSTDOUT、およびSTDERRをオーバーライドできます。パイプ
を 作成し、子のSTDOUTをこのパイプにリダイレクトします。

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

オプション ハッシュに注意してください: {:out => wr} - STDOUTwrストリーム記述子にリダイレクトするように 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 ミューテックスではなく) を使用して、このパイプへのアクセスを制御する必要があります。

于 2012-11-06T19:32:26.313 に答える
1

メソッドを使用できますIO::pipe

子プロセスを作成する最良の方法ではないことを選択したと思います。バックティックはバックティックを行いforkexec舞台裏とrubyコマンドもfork行いexecます。これは、コマンドが次のことを意味します。

`ruby -r a_lib -e 'a_method'`

現在のプロセスを fork し、シェル プロセスに変換し、シェル プロセスを fork し、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 に答える