4

Rubyでは、Process.spawnを使用して、新しいプロセスでコマンドを実行しています。生成されたプロセスからstdoutとstderrをキャプチャするために、双方向パイプを開きました。これは、パイプに書き込まれるバイト(コマンドからのstdout)が64Kbを超えるまではうまく機能し、64Kbを超えると、コマンドは終了しません。パイプバッファサイズがヒットし、パイプへの書き込みがブロックされ、プロセスが終了しないと考えています。私の実際のアプリケーションでは、プロセスが終了したときにキャプチャして保存する必要のある多くのstdoutを含む長いコマンドを実行しています。バッファサイズを大きくする方法はありますか、それとも制限に達しないようにバッファをフラッシュする方法はありますか?

cmd = "for i in {1..6600}; do echo '123456789'; done"  #works fine at 6500 iterations.

pipe_cmd_in, pipe_cmd_out = IO.pipe
cmd_pid = Process.spawn(cmd, :out => pipe_cmd_out, :err => pipe_cmd_out)

Process.wait(cmd_pid)
pipe_cmd_out.close
out = pipe_cmd_in.read
puts "child: cmd out length = #{out.length}"

アップデート Open3 :: caption2eは、私が示した単純な例では機能するようです。残念ながら、実際のアプリケーションでは、生成されたプロセスのpidも取得でき、実行をブロックするタイミングを制御できる必要があります。一般的な考え方は、ノンブロッキングプロセスをフォークすることです。このフォークで、コマンドを生成します。コマンドpidを親プロセスに送り返し、コマンドが終了するのを待って終了ステータスを取得します。コマンドが終了すると、終了ステータスが親に返送されます。親では、ループが1秒ごとに繰り返され、一時停止や再開などの制御アクションについてDBをチェックします。制御アクションを取得すると、適切な信号をコマンドpidに送信して停止し、続行します。コマンドが最終的に終了すると、親はレスキューブロックにヒットし、終了ステータスパイプを読み取り、DBに保存します。実際のフローは次のようになります。

#pipes for communicating the command pid, and exit status from child to parent
pipe_parent_in, pipe_child_out = IO.pipe
pipe_exitstatus_read, pipe_exitstatus_write = IO.pipe

child_pid = fork do
    pipe_cmd_in, pipe_cmd_out = IO.pipe
    cmd_pid = Process.spawn(cmd, :out => pipe_cmd_out, :err => pipe_cmd_out)
    pipe_child_out.write cmd_pid  #send command pid to parent
    pipe_child_out.close
    Process.wait(cmd_pid)
    exitstatus = $?.exitstatus
    pipe_exitstatus_write.write exitstatus  #send exitstatus to parent
    pipe_exitstatus_write.close
    pipe_cmd_out.close
    out = pipe_cmd_in.read
    #save out to DB
end

#blocking read to get the command pid from the child
pipe_child_out.close
cmd_pid = pipe_parent_in.read.to_i

loop do
    begin
        Process.getpgid(cmd_pid)  #when command is done, this will except
        @job.reload #refresh from DB

        #based on status in the DB, pause / resume command
        if @job.status == 'pausing'
            Process.kill('SIGSTOP', cmd_pid)
        elsif @job.status == 'resuming'
            Process.kill('SIGCONT', cmd_pid)
        end
    rescue
        #command is no longer running
        pipe_exitstatus_write.close
        exitstatus = pipe_exitstatus_read.read
        #save exit status to DB
        break
    end
    sleep 1
end

注:親はパイプが閉じるのを待ってブロックされるため、親にコマンド出力パイプをポーリングさせることはできません。制御ループを介してコマンドを一時停止および再開することはできません。

4

2 に答える 2

1

このコードは、あなたが望むことをしているようで、実例かもしれません。

cmd = "for i in {1..6600}; do echo '123456789'; done"

pipe_cmd_in, pipe_cmd_out = IO.pipe
cmd_pid = Process.spawn(cmd, :out => pipe_cmd_out, :err => pipe_cmd_out)

@exitstatus = :not_done
Thread.new do
  Process.wait(cmd_pid); 
  @exitstatus = $?.exitstatus
end

pipe_cmd_out.close
out = pipe_cmd_in.read;
sleep(0.1) while @exitstatus == :not_done
puts "child: cmd out length = #{out.length}; Exit status: #{@exitstatus}"

一般に、スレッド間でデータを共有する (@exitstatus) にはより注意が必要ですが、初期化後にスレッドによって一度だけ書き込まれるため、ここでは機能します。($?.exitstatus は nil を返す可能性があることが判明したため、別のものに初期化しました)プロセスが標準出力を閉じました。

于 2012-12-12T18:21:45.380 に答える
0

確かに、あなたの診断はおそらく正しいです。プロセスが終了するのを待っている間に、パイプにselect and readループを実装することもできますが、stdlibを使用すると、必要なものをより簡単に取得できる可能性がありますOpen3::capture2e

于 2012-12-11T23:03:03.650 に答える