2012-02-21 10 views
2

Runtime.exec()を使用してjavaでサブプロセスを作成する場合、サブプロセスのブロックを防ぐために入出力ストリームを埋める必要があることを認識しています。Java、サブプロセス、未読出力ストリーム:デッドロックはいつですか?

興味深いことに、Process状態のjavadocをもう少し:

...failure to promptly write the input stream or read the output stream of 
the subprocess may cause the subprocess to block, and even deadlock. 

私はこのような状況では、サブプロセスがをデッドロックすることができますことを疑問に思って!

質問:
1.デッドロックする条件はありますか?
2.なぜデッドロックですか?
3.このデッドロックを示す短いサンプルプログラムを提供できますか?
4.このデッドロックはOSのバグですか?

+0

+1 ...(あなたの質問に対する答えではないのでコメント)* ...長い時間前(私が*「Runtime.execが* IIRCではない」と読んだとき) Runtime.exec'edシェルスクリプトを直ちにフォーク/終了させ、第2のシェルスクリプトからの出力を一時ファイルにリダイレクトする(そして、これらの一時ファイルをJavaから解析する)ことによって、この問題を完全に回避することができます。残念だけど、うまくいきます(LinuxとOS Xの両方でやったことがあります... Windowsについて知らないでください): – TacticalCoder

答えて

1

親は、出力のいずれかを読む前に、親が子の入力ストリームにあまりにも多くのデータを送信しようとすると、制限されたバッファサイズのためにデッドロックが発生する可能性があります。

このコード考えてみましょう:linesのための小さな値について

final int LINES = 10; 
// "tr" is a Unix command that translates characters; 
// Here, for every line it reads, it will output a line with 
// every 'a' character converted to 'A'. But any command that outputs 
// something for every line it reads (like 'cat') will work here 
Process p = Runtime.getRuntime().exec("tr a A"); 
Writer out = new OutputStreamWriter(p.getOutputStream()); 
for (int i = 0; i < LINES; i++) { 
    out.write("abcdefghijklmnopqrstuvwxyz\n"); 
} 
out.close(); 
// Read all the output from the process and write it to stdout 
BufferedReader in = new BufferedReader(new InputStreamReader(p.getInputStream())); 
String line; 
while ((line = in.readLine()) != null) { 
    System.out.println(line); 
} 

を、それが正常に動作します。 trの出力はすべて、読み込みを開始する前にOSバッファに収まるようにすることができます。しかし、大きな値(> 10000で十分でなければなりません)では、OSバッファがいっぱいになります。 trコマンド内でwriteの呼び出しがブロックされ、バッファが空になるのを待ってから、Javaコードが書き込んでいるバッファがいっぱいになります(trがブロックされ、入力から読み取れないため)次にブロックを呼び出してブロックout.writeをブロックすると、両方のプロセスがアクティブに読み込まれていないフルバッファに書き込むのを待っているデッドロックにつながります。

このデッドロックはOS内のバグではありません。プロセス間通信のバッファサイズが制限されているのは意図的な設計の決定であるためです。バッファ自動的に「オーバー流出」はディスクに、それは予測不可能なパフォーマンスにつながることができ、潜在的に埋めることができれば、

  • は、上記のを避けるためにカーネルメモリを使い果たすことができます:代替(無制限のバッファサイズ)は、いくつかの欠点を持っていますディスク。余談として

、デッドロックはまた、インプロセスのバッファに起因する起こり得ます。上記のデッドロックに対処するために、Javaコードを変更して1行を書き込んだ後、1​​行を交互に読み取るとします。しかし、it's common for Linux processes to not flush after every line when they're not writing directly to a terminal。したがって、trは、行を読み込んでlibc出力バッファに書き込んだり、次の行が書き込まれるのを待ってブロックしたりすることがあります。そして、Javaコードは、trが行を出力するのを待ってブロックします。

1

デッドロックには常に少なくとも2人の参加者が関係します。つまり、デッドロックの可能性があるサブプロセスだけではなく、親プロセスとサブプロセスの組み合わせです。例えば

サブプロセスライン、プロセス、それを読み、その結果を印刷したいです。 親プロセスは、まずサブプロセスからの出力を読み込み、それに入力を提供できると考えます。

結果:デッドロックで、両方のプロセスが他方からの入力を待機しています。

これはOSのバグではなく、(親)プロセスの論理エラーです。

+0

この種の問題がjavadocによって意図されているかどうかはわかりません。記述は並行環境での一般的な同期の問題ですが、特にjava/Processクラスとは関係ありません。 javadocでは、これは、「限られたバッファサイズしか提供しないネイティブプラットフォームでは、」という状況がすべてのOSでブロックされるのに対し、 – MRalwasser

+0

@Malwaser - もしそうなら、私はそれをOSのバグだと考えています。 「即座に」は少しは非特異的です。コミュニケーションが正しい順序で行われている限り、私がそれを「速やかに」行うのか2日後に行うのかは関係ありません。 – Ingo

関連する問題