3

これが私のシナリオです:

  1. プロセス A は子プロセス B を生成し、スレッドをスピンして B の出力をドレインします。
  2. プロセス B はデーモン プロセス C を生成し、その出力も排出します。
  3. プロセス B が終了しても、デーモン プロセスは存続します。
  4. プロセス A は、プロセス B が process.waitFor() を介して終了したことを検出します。ただし、プロセス B の入力ストリームの読み取りでスタックしています。これは、B がデーモンを開始したためです。入力ストリームは、プロセス C が終了したときにのみ EOF を受け取ります。

これは Windows でのみ発生します。ProcessBuilder を使用しています。私が思いついた解決策は次のとおりです。私が本当に気に入っている解決策はありませんので、フィードバックをお寄せください。

  1. jna を使用してデーモン プロセス C を生成できます。このようにして、「十分に切り離された」プロセスを作成でき、プロセス A は B からのストリームの排出に固執しません。それは機能しますが、私はその解決策にあまり熱心ではありません。それはいくつかのネイティブ コードを意味します (そして、私は入力を消費することに熱心なので、その多くを意味します)。JNAを介してそれを行う方法のインスピレーションは、http: //yajsw.sourceforge.netにあります(ただし、単なるプロセス開始よりもはるかに多くのものが含まれています)。
  2. jre7 で実行します。Jdk7 は ProcessBuilder にいくつかの新しい機能をもたらします。たとえば、私の問題も解決する inheritIO() などです。どうやら、inheritIO() がオンになっていると、デーモン プロセス C のすべてのストリームを単純に閉じることができ (これはデーモンなのでとにかくそうします)、問題は解決します。ただし、jre5+で実行する必要があります
  3. デーモン プロセス C を生成する前に、プロセス B の System.out と System.err を閉じます。これで問題は解決しますが、これらのストリームに有用なものを書き込むため、プロセス B でこれらのストリームを動作させる必要があります。ダメ。B と C の間にある種のブートストラップ プロセスを配置することで、この特性を利用できることを望んでいましたが、問題は解決しませんでした。
  4. Linux ではその問題は発生しないので、Linux でのみ実行できますか? いいえ、できません。
  5. プロセスAがプロセスBの出力をブロックしない方法で排出するようにしました。これはある程度機能しますが、便利ではありません。たとえば、inputStream.read() は中断できません。inputStream.available() を使用できますが、EOF と zero-bytes-available を区別しません。したがって、プロセス A が B の出力 EOF に関心がない場合にのみ、解決策は適切です。また、このソリューションはより CPU を集中的に使用するようであり、一般的には... ぎこちなく、防弾とは言えません。
  6. プロセス C を --dry-run モードで実行します。このモードでは、開始できるかどうかを確認するだけです。起動を試み、ウェルカム メッセージを送信して終了します。長時間実行されなくなったため、読み取りをブロックしません。プロセス B は、C を開始できるという十分な信頼を得ることができ、比較的単純な JNA コードを使用して、その出力を消費せずに切り離されたプロセスを生成できます (出力を消費すると、JNA 関連のコードが煩雑で重くなります)。唯一の問題は、C の出力をコンシューマ処理しなくなったことですが、プロセス B がコンシューマできる既知のファイルに C を書き込むことで解決できます。このソリューションは、大きくて醜い回避策のようなものですが、私たちにとっては実行可能です. とにかく、私たちは解決策 1) を現在試しています。

ヒントをいただければ幸いです。

4

1 に答える 1

0

同じ問題が発生しました。問題の回避策があると思います。プロセス A では、Process.waitFor() の後に次のコード フラグメントがあります。ここで、outT と errT は、プロセス B の stdout と stderr をそれぞれ読み取るスレッドです。

try {
  outT.join(1000);
  if (outT.isAlive()) {
    errmsg("stdout reader still alive, interrupting", null);
    outT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from out stream reader: "+e, e);
}
try {
  errT.join(1000);
  if (errT.isAlive()) {
    errmsg("stderr reader still alive, interrupting", null);
    errT.interrupt();
  }
} catch (Exception e) {
  errmsg("Exception caught from err stream reader: "+e, e);
}
p.destroy();

p.destroy() が必要かどうかはわかりませんが、問題に対処するためにあらゆる種類の組み合わせを試してきました。

とにかく、outT/errT スレッドの run() メソッドには、次のようなものがあります。「pipe」変数は、サブプロセスの stdout/stderr をキャプチャする Writer インスタンスです。「in」変数は、Process から取得した stdout または stderr ストリームです。

try {
  r = new BufferedReader(new InputStreamReader(in, enc));
  String line;
  while (true) {
    if (Thread.currentThread().isInterrupted()) {
      errmsg("Text stream reader interrupted", null);
      break;
    }
    if (r.ready()) {
      line = r.readLine();
      if (line == null) {
        break;
      }
      pipe.write(line);
      pipe.write(SystemUtil.EOL);
      if (autoFlush) {
        pipe.flush();
      }
    }
  }
  pipe.flush();

} catch (Throwable t) {
  errmsg("Exception caught: "+t, t);
  try { pipe.flush(); } catch (Exception noop) {}

} finally {
  IOUtil.closeQuietly(in);
  IOUtil.closeQuietly(r);
}

サブプロセスが終了した後でも、サブプロセスからEOFの兆候を決して得ないようです。したがって、古いスレッドとブロックを防ぐために上記のすべてのチカネリが行われます。

于 2012-08-15T08:35:13.610 に答える