名前付きパイプを使用してプロセス間で渡す方法は?

名前付きパイプを使用してプロセス間で渡す方法は?

/tmp/in、プロセスによって生成され開かれた/tmp/out名前付きパイプです(それぞれ読み取り、書き込み、書き込み用)。/tmp/err

私は新しいプロセスを作成し、そのstdinをそれにパイプし/tmp/in、その内容をstdoutに書き、可能であれば/tmp/outその内容をstderrに書きたいと思います。/tmp/errすべてが一つでなければなりません。ラインバッファファッション。このプロセスは、作成された他のプロセスが/tmp/in読み取りを停止して閉じたときに終了する必要があります/tmp/in。このソリューションは、追加のパッケージをインストールせずにUbuntuで実行する必要があります。 bashスクリプトで解決したいです。


マックサイフそうでなければ指摘SSCCE、私が欲しいものを理解するのは難しいです。以下はSSCCEです。しかし、これは最小限の例なので、非常に愚かなことに注意してください。

元の設定

親プロセスは子プロセスを開始し、子プロセスのstdinとstdoutを介して1行ずつ通信します。これを実行すると、次のような結果が得られます。

$ python parent.py 
Parent writes to child:  a
Response from the child: A

Parent writes to child:  b
Response from the child: B

Parent writes to child:  c
Response from the child: C

Parent writes to child:  d
Response from the child: D

Parent writes to child:  e
Response from the child: E

Waiting for the child to terminate...
Done!
$ 

parent.py

from __future__ import print_function
from subprocess import Popen, PIPE
import os

child = Popen('./child.py', stdin=PIPE, stdout=PIPE)
child_stdin  = os.fdopen(os.dup(child.stdin.fileno()), 'w')
child_stdout = os.fdopen(os.dup(child.stdout.fileno()))

for letter in 'abcde':
    print('Parent writes to child: ', letter)
    child_stdin.write(letter+'\n')
    child_stdin.flush()
    response = child_stdout.readline()
    print('Response from the child:', response)
    assert response.rstrip() == letter.upper(), 'Wrong response'

child_stdin.write('quit\n')
child_stdin.flush()
print('Waiting for the child to terminate...')
child.wait()
print('Done!')

子供.py,実行可能でなければなりません!

#!/usr/bin/env python
from __future__ import print_function
from sys import stdin, stdout

while True:
    line = stdin.readline()
    if line == 'quit\n':
        quit()
    stdout.write(line.upper())
    stdout.flush()

必須設定とハッキングソリューション

親ソースファイルまたは子ソースファイルはすべて編集できません。

child.pyの名前をchild_original.pyに変更し、実行可能にしました。その後、実行する前にchild_original.py自体を起動するchild.py(プロキシまたはメディエータ)と呼ばれるbashスクリプトを配置し、python parent.pyParent.pyに偽のchild.pyを呼び出すようにしました。今私のbashスクリプトは親間で渡されます。 .pyとchild_original.py。

fakechild.py

#!/bin/bash
parent=$$
cat std_out &
(head -n 1 shutdown; kill -9 $parent) &
cat >>std_in

start_child.sh親プロセスを実行する前にchild_original.pyを起動してください。

#!/bin/bash
rm -f  std_in std_out shutdown
mkfifo std_in std_out shutdown
./child_original.py <std_in >std_out
echo >shutdown
sleep 1s
rm -f  std_in std_out shutdown

実行方法:

$ ./start_child.sh & 
[1] 7503
$ python parent.py 
Parent writes to child:  a
Response from the child: A

Parent writes to child:  b
Response from the child: B

Parent writes to child:  c
Response from the child: C

Parent writes to child:  d
Response from the child: D

Parent writes to child:  e
Response from the child: E

Waiting for the child to terminate...
Done!
$ echo 

[1]+  Done                    ./start_child.sh
$ 

このハッキングソリューションは動作します。私が知っている限り、ラインバッファリングの要件を満たしておらず、child_original.pyがパイプを閉じて、start_child.shが安全にシャットダウンされる可能性があることをstart_child.shに知らせる追加の閉じるfifoがあります。


質問では、要件(ラインバッファリング、child_original.pyがパイプを閉じるときにシャットダウン、追加のパイプを閉じる必要はありません)を満たす偽のchild.py bashスクリプトの改善を求めます。



私が知りたいこと:

  • 高度なAPIを使用してfifoをファイルとして開く場合は、そのファイルを開く必要があります。読み書き、そうでない場合、呼び出しはブロックopenされました。これは非常に直感的です。また、見ることができます名前付きパイプブロックを読み取り専用で開くのはなぜですか?
  • 実際、私の親プロセスはJavaアプリケーションです。 Javaの外部プロセスを使用している場合は、次の場所から外部プロセスのstdoutとstderrを読みます。悪魔スレッド(setDamon(true)このスレッドを呼び出します。今後始めてください)。それ以外の場合、すべての操作が完了してもJVMは永久に停止します。質問には関係ありませんが、他のトラップには以下が含まれます。Runtime.exec() メソッドに関連するトラップバイパス
  • 明らかにバッファリングされていないことはバッファリングされることを意味しますが、バッファがいっぱいになるのを待つのではなく、できるだけ早くフラッシュします。

ベストアンサー1

シャットダウンとシャットダウン機能を削除すると(安全ではありませんが極端ですが理解できない場合、サブシェルは一部の無実のプロセスを終了する前に死亡する可能性がありますchild.py)、終了しません。素晴らしいUNIX市民のように振る舞います。(head -n 1 shutdown; kill -9 $parent) &kill -9child.pyparent.py

メッセージを送信すると、子プロセスは作成者であるためcat std_out &完了します。メッセージを受信すると完了し、この時点でパイプであるパイプを閉じてサブプロセスを完了できます。quitstd_outchild_original.pyquitstdoutstd_outclosecat

cat > std_inprocess で始まるパイプから読み取っているため、まだ完了しておらず、parent.pyプロセスparent.pyはパイプを閉じるのに気を遣っていません。その場合cat > stdin_in、プロセス全体がそれchild.py自体で完了し、パイプやセクションを閉じる必要はありません。killing(高速化による競合状態のため、UNIXで子供以外のプロセスを終了することは常に潜在的なセキュリティホールです。)リサイクルが必要です。

パイプの右端のプロセスは通常、標準入力を読み取った後にのみ完了しますが、そのプロセス()を閉じないため、child.stdin暗黙的に子プロセスに「待ってください。なので終了しても大丈夫です。

つまり、parent.py行動を合理的にしてください。

from __future__ import print_function
from subprocess import Popen, PIPE
import os

child = Popen('./child.py', stdin=PIPE, stdout=PIPE)

for letter in 'abcde':
    print('Parent writes to child: ', letter)
    child.stdin.write(letter+'\n')
    child.stdin.flush()
    response = child.stdout.readline()
    print('Response from the child:', response)
    assert response.rstrip() == letter.upper(), 'Wrong response'

child.stdin.write('quit\n')
child.stdin.flush()
child.stdin.close()
print('Waiting for the child to terminate...')
child.wait()
print('Done!')

child.pyこんなに簡単にできます。

#!/bin/sh
cat std_out &
cat > std_in
wait #basically to assert that cat std_out has finished at this point

child.stdin(fd dup呼び出しを削除した理由は、そうでなければ両方の呼び出しと冗長呼び出しの両方を閉じる必要があるためですchild_stdin)。

parent.pygnuはライン指向の方法で実行されるため、gnuはバッファcatリングされず(mikeservが指摘したように)ライン指向のchild_original.py方法で動作するため、実際にライン全体がバッファリングされます。


猫について注意すべき点:catUnbufferedはgnuがバッファを使用するので、おそらく最も幸運な用語ではないでしょう。この機能が実行しないことは、(stdioとは異なり)コンテンツを書き込む前にバッファ全体を埋めることです。デフォルトでは、特定のサイズ(バッファサイズ)の読み取り要求をオペレーティングシステムに送信し、ライン全体またはバッファ全体を取得するのを待たずに受信したすべての内容を書き込みます。 (読書(2)怠惰に要求されたバッファ全体ではなく、現在提供できるものだけを提供できます。 )

(ソースコードは以下から確認できます。http://git.savannah.gnu.org/cgit/coreutils.git/tree/src/cat.c; safe_read(通常の代わりに使用read)はgnulibサブモジュールにあり、非常に単純なラッパーです。読書(2)抽象化されましたEINTR(manページを参照)。

おすすめ記事