2

いくつかの作業を並行して実行したいbashスクリプトがあります。バックグラウンドで実行されるサブシェルに各ジョブを配置することでこれを行いました。同時に実行されるジョブの数はある程度制限されているはずですが、最初にいくつかの行を FIFO に入れ、次にサブシェルをフォークする直前に、親スクリプトがこの FIFO から行を読み取る必要があります。サブシェルをフォークできるのは、行を取得した後でのみです。これまでのところ、すべて正常に動作しています。しかし、サブシェルで FIFO から行を読み込もうとすると、FIFO にもっと多くの行があるように見えても、行を取得できるのは 1 つのサブシェルだけのようです。FIFOにさらに行がある場合でも、他のサブシェルが行を読み取れないのはなぜでしょうか。
私のテストコードは次のようになります。


#!/bin/sh

fifo_path="/tmp/fy_u_test2.fifo"
mkfifo $fifo_path
#open fifo for r/w at fd 6
exec 6<> $fifo_path

process_num=5
#put $process_num lines in the FIFO

for ((i=0; i<${process_num}; i++)); do
    echo "$i"
done >&6

delay_some(){
    local index="$1"
    echo "This is what u can see. $index \n"
    sleep 20;
}

#In each iteration, try to read 2 lines from FIFO, one from this shell,
#the other from the subshell
for i in 1 2
do
    date >>/tmp/fy_date
#If a line can be read from FIFO, run a subshell in bk, otherwise, block.
    read -u6
    echo " $$ Read --- $REPLY  --- from 6 \n" >> /tmp/fy_date
    {
        delay_some $i
#Try to read a line from FIFO, __ only one subshell succeeds the following line. __
        read -u6
        echo " $$ This is in child # $i, read --- $REPLY --- from 6 \n" >> /tmp/fy_date
    } &
done


出力ファイル /tmp/fy_date の内容は次のとおりです。


Mon Apr 26 16:02:18 CST 2010
 32561 Read --- 0  --- from 6 \n
Mon Apr 26 16:02:18 CST 2010
 32561 Read --- 1  --- from 6 \n
 32561 This is in child # 1, read --- 2 --- from 6 \n

そこでは、次のような行が期待されます。


 32561 This is in child # 2, read --- 3 --- from 6 \n

しかし、それは表示されず、発行するまで子 #2 プロセスがそこでブロックされます:
echo something > /tmp/fy_u_test2.fifo

4

7 に答える 7

1

'read-u6'シェル呼び出しと関係があるようです。シェルのSTDINを閉じている場合、「read -u6」が発行されると、fd 6から128バイトを読み取ろうとします。ただし、STDINをそのままにしておくと、「read -u6」が発行されると、バイトが1つずつ読み取られます。 '\n'に遭遇するまで1つ。この奇妙なアクションを「strace」から発見しました。最初のケースでは、「read-u6」呼び出しによって次のシステムコールが発生しました。

read(6, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n"..., 128) = 50

後者の場合、「read -u6」呼び出しにより、次のシステムコールが発生しました。

30371 16:27:15 read(6, "0", 1)          = 1
30371 16:27:15 read(6, "\n", 1)         = 1

テストコードは次のとおりです。


#!/bin/bash

fifo_path="/tmp/fy_u_test2.fifo"
mkfifo $fifo_path
#open fifo for r/w at fd 6
exec 6<> $fifo_path

#comment or decomment the following line makes difference
exec 0>&-

process_num=20
#put $process_num lines in the FIFO
for ((i=0;i<${process_num};i++));do
    echo "$i"
done >&6

delay_some(){
    local index="$1"
    echo "This is what u can see. $index \n"
    sleep 10;
}

#In each iteration, try to read 2 lines from FIFO, one from this shell,
#the other from the subshell
for i in 1 2 3
do
    date >>/tmp/fy_date
#If a line can be read from FIFO, run a subshell in bk, otherwise, block.
    read -u6
    echo " $$ Read --- $REPLY  --- from 6 \n" >> /tmp/fy_date
    {
        delay_some $i
#Try to read a line from FIFO
#   read -u6
        echo " $$ This is in child # $i, read --- $REPLY --- from 6 \n" >> /tmp/fy_date
        echo " $$ Again this is in child # $i, read --- $REPLY --- from 6 \n" >> /tmp/fy_date
        echo "$i xx" >&6
#       echo xx >&6
    } &
done

#sleep 13
#wait
#read -u6
echo "$$ After fork, in parent, read --- $REPLY --- from 6 \n" >> /tmp/fy_date
于 2010-04-28T08:47:04.577 に答える
1

POSIX システムの FIFO は基本的に名前付きパイプであることに注意してください。パイプ上でデータを移動するには、一方にリーダーが必要で、もう一方にライターが必要です。一方が閉じられると、もう一方は役に立たなくなります。

catつまり、 FIFO の内容がなくなるため、他のリーダーが終了した後に FIFO を使用することはできません。

通常のファイルの使用について確認したり (ファイルのロックを使用して、その通常のファイルへのアクセスを同期していることを確認したり)、複数のファイルを含むディレクトリを使用したり、共有メモリまたはそれに類似したものを使用したりすることもできます (ただし、おそらくシェルスクリプトではありません)。それはすべて、最終目標が何であるか、実際には、それを実現するための最良の方法が何であるかによって異なります.

于 2010-04-26T15:54:38.623 に答える
1

fifoへの書き込みでバッファリングが行われている可能性はありますか? unbuffer を利用できる場合は、エコーの前にそれを付けてみていただけますか? ここでどのように発生するかはわかりませんが、症状が当てはまるので、試してみる価値があります。

于 2010-04-26T15:44:59.140 に答える
0

ここで他の回答で説明されている理由により、パイプからの読み取りと書き込みを同時に実行できない限り、パイプは必要ありません。

したがって、IPCの別の手段を使用するか、fifosの使用法を再構築して、メインプロセスがワークプロセスを作成している間(またはその逆)に非同期プロセスがパイプを埋めるようにすることをお勧めします。

単純なファイルを一種のキューとして使用して、必要なものを取得する方法は次のとおりです。

#!/usr/bin/env bash

stack=/tmp/stack
> "$stack"

# Create an initial 5 spots on the stack
for i in {1..5}; do
    echo >> "$stack"
done

for i in {1..10}; do
    # Wait for a spot on the stack.
    until read; do sleep 1; done

    {
        echo "Starting process #$i"
        sleep $((5 + $i)) # Do something productive
        echo "Ending process #$i"

        # We're done, free our spot on the stack.
        echo >> "$stack"
    } &
done < "$stack"

補足:このメソッドは、呼び出すプロセスごとにスタックファイルにバイトを追加するため、無制限の作業には理想的ではありません。つまり、スタックファイルの成長が遅くなります。

于 2010-08-05T11:02:59.707 に答える
0

実行すると、ログ ファイルに 4 行すべてが記録されます。シバンを に変更するとどうなります#!/bin/bashか?

于 2010-04-26T10:49:36.497 に答える
0

これは、両方のサブシェルが同じ fifo から同時に読み取ろうとする同時実行の問題である可能性があります。それはいつも起こりますか?

ステートメントを追加するflock -x 6か、2 つのサブシェルの遅延を変更して、何が起こるかを確認できます。

ところで、bash 3.2 とカーネル 2.6.28 でコードが正常に動作することを確認できます。

于 2010-04-26T11:38:30.750 に答える
0

親シェルが終了すると、親シェルが失われたときに FIFO に未読のままのデータが見つかりました。
次のコードがある場合:


#!/bin/sh

fifo_path="/tmp/fy_u_test2.fifo"
mkfifo $fifo_path
#open fifo for r/w at fd 6
exec 6<> $fifo_path

process_num=9
#put $process_num lines in the FIFO

for ((i=0;i<${process_num};i++));do
echo "$i"
done >&6

for i in 1 2 3;
do
 read -u6
done

このコードが終了した後、コマンド 'cat /tmp/fy_u_test2.fifo' は何も与えません。
しかし、次のコードがある場合。


#!/bin/sh

fifo_path="/tmp/fy_u_test2.fifo"
mkfifo $fifo_path
#open fifo for r/w at fd 6
exec 6<> $fifo_path

process_num=9
#put $process_num lines in the FIFO

for ((i=0;i<${process_num};i++));do
echo "$i"
done >&6

for i in 1 2 3;
do
 read -u6
done
#__ notice this line __
sleep 60

このコードを発行して実行した後、60 秒間のスリープ中にコマンド「cat /tmp/fy_u_test2.fifo」を実行すると、次の出力が得られます。

$ 猫/tmp/fy_u_test2.fifo
3
4
5
6
7
8
于 2010-04-26T12:23:33.543 に答える