web-dev-qa-db-ja.com

Linuxの「漏れやすい」パイプ

次のようなパイプラインがあるとします。

_$ a | b_

bがstdinの処理を停止すると、しばらくするとパイプがいっぱいになり、aからそのstdoutへの書き込みがブロックされます(bが再び処理を開始するか、終了するまで)。

これを回避したい場合は、次のように大きなパイプ(または、もっと簡単に言えば、buffer(1))を使用したくなるでしょう。

_$ a | buffer | b_

これは単に私にもっと時間を費やすでしょうが、結局aは結局止まるでしょう。

(私が取り組んでいる非常に具体的なシナリオの場合)欲しいのは、「リークの多い」パイプを使用して、フルのときにデータを(理想的には行ごとに)バッファーからドロップしてaを許可することです処理を続行します(おそらくご想像のとおり、パイプを流れるデータは使い捨てです。つまり、bでデータを処理することは、aがブロックなしで実行できることよりも重要ではありません)。

要約すると、私は制限された、漏れのあるバッファのようなものが欲しいです:

_$ a | leakybuffer | b_

私はおそらくそれをどの言語でも非常に簡単に実装できたでしょう。私が欠けている「すぐに使える」もの(またはbashワンライナーのようなもの)があるかどうか疑問に思っていました。

注:例では通常のパイプを使用していますが、質問は名前付きパイプにも同様に当てはまります


私は以下の回答を与えましたが、以下の簡単な解決策にはいくつかの制限があるため、leakybufferコマンドを実装することも決定しました: https://github.com/CAFxX/leakybuffer

12
CAFxX

最も簡単な方法は、ノンブロッキング出力を設定するプログラムをパイプでつなぐことです。これは単純なPerlワンライナー(leakybufferとして保存できます)です。

したがって、_a | b_は次のようになります。

_a | Perl -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | b
_

入力を読み取り、出力に書き込む(cat(1)と同じ)が、出力はノンブロッキングです。つまり、書き込みが失敗すると、エラーが返され、データが失われますが、プロセスは次の行に進みます。エラーを無視するので便利です。プロセスは、必要に応じてラインバッファリングされますが、以下の警告を参照してください。

あなたは例えばでテストすることができます:

_seq 1 500000 | Perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { print }' | \
    while read a; do echo $a; done > output
_

失われた行を含むoutputファイルを取得します(正確な出力はシェルの速度などに依存します)。

_12768
12769
12770
12771
12772
12773
127775610
75611
75612
75613
_

シェルは_12773_の後に行を失った場所を確認できますが、異常も発生します-Perlには_12774\n_のための十分なバッファーがありませんでしたが、_1277_のために十分なバッファーがありませんでした。次の数値_75610_は行の先頭から開始されないため、見苦しくなります。

これは、書き込みが完全に成功しなかったときにPerlに検出させ、後で新しい行を無視して残りの行をフラッシュしようとすることで改善できますが、Perlスクリプトがさらに複雑になるため、次の演習として残します。興味のある読者:)

pdate(バイナリファイルの場合):改行で終了する行(ログファイルなど)を処理しない場合は、コマンドを少し変更する必要があります。そうしないと、Perlが大量のメモリを消費します(改行の頻度によって異なります)。入力に文字が表示されます):

_Perl -w -MFcntl -e 'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (read STDIN, $_, 4096) { print }' 
_

バイナリファイルでも正しく機能します(余分なメモリを消費することはありません)。

pdate2-より良いテキストファイル出力:出力バッファの回避(syswriteの代わりにprint):

_seq 1 500000 | Perl -w -MFcntl -e \
    'fcntl STDOUT,F_SETFL,O_NONBLOCK; while (<STDIN>) { syswrite STDOUT,$_ }' | \
    while read a; do echo $a; done > output
_

私にとって「マージされた行」の問題を修正するようです:

_12766
12767
12768
16384
16385
16386
_

(注:出力がどの行でカットされたかを確認できます:_Perl -ne '$c++; next if $c==$_; print "$c $_"; $c=$_' output_ oneliner)

14
Matija Nalis