关于FIFO和文件描述符的问题

5
我写了这样一个脚本:

我写了这样一个脚本:

N=5
FIFO=/tmp/$$.fifo
mkfifo $FIFO

loop(){
    for i in $(seq 1 $N); do
        read tmp < $FIFO
        echo "$i out"
    done
}

loop &
LOOP_PID=$!
for i in $(seq 1 $N); do
    echo $i > $FIFO
    echo "$i in"
done
wait $LOOP_PID

当我运行这个脚本时,它会停在wait $LOOP_PID处,无法继续执行。因此,我使用文件描述符修改了这个脚本:
N=5
FIFO=/tmp/$$.fifo
mkfifo $FIFO
exec 3<>$FIFO

loop(){
    for i in $(seq 1 $N); do
        read -u3 tmp
        echo "$i out"
    done
}

loop &
LOOP_PID=$!
for i in $(seq 1 $N); do
    echo $i >&3
    echo "$i in"
done
wait $LOOP_PID

这还好。

当我直接使用FIFOs时,它无法连续读取FIFOs中的数据,并且会挂起。但是当我使用文件描述符时,一切都正常。这是什么原因呢?

1个回答

4

替换这个:

loop(){
    for i in $(seq 1 $N); do
        read tmp < $FIFO
        echo "$i out"
    done
}

有了这个:

loop(){
    for i in $(seq 1 $N); do
        read tmp
        echo "$i out"
    done < $FIFO
}

这样做可以保持FIFO开放,而不是在每次循环中重新打开和关闭它。

FIFO很棘手:

  1. 尝试向FIFO写入内容时,除非另一个进程准备从FIFO读取内容,否则会被阻塞。

  2. 如果正在读取FIFO的进程关闭了FIFO,则任何未读取的信息都将丢失。

这意味着你上面的脚本如何运行可能取决于时间的偶然性。当执行 read tmp < $FIFO 时,有多少行已经写入到FIFO中?read 只会读取第一行,而当FIFO被关闭时,其余的信息将被丢弃。

如何使用 exec 语句

让我们比较两个脚本。第一个直接使用FIFO:

#!/bin/sh
fifo=/tmp/$$.myfifo
mkfifo "$fifo"
echo $'1\n2\n3\n4'>"$fifo"

for i in {1..4}
do
    read  tmp
    echo $tmp
done <"$fifo"

上述脚本在第一个 echo 输出时会挂起,等待某个进程开始读取FIFO。由于它卡在那里,因此永远不会执行read tmp语句,这个脚本将没有任何输出。
第二个脚本使用exec创建文件句柄:
#!/bin/sh
fifo=/tmp/$$.myfifo
mkfifo "$fifo"
exec 3<>"$fifo"
echo $'1\n2\n3\n4'>&3

for i in {1..4}
do
    read -u3 tmp
    echo $tmp
done

这个脚本不会挂起并且会产生四行输出。区别在于shell提供了文件句柄的缓冲。因此,当第一个echo语句尝试写入FIFO时,shell已准备好从FIFO中读取。shell已经读取的数据可以被read -u3 tmp语句使用。


还好。但当我像这样向FIFO写入数据:sleep 1; echo $i > $FIFO时,它就不起作用了。 - solomon_wzs
我将代码替换为 until read tmp,让它循环直到读取到非空行,这样做是可以的。但我仍然有一个问题,为什么文件描述符可以很好地完成这项工作? - solomon_wzs
@solomon_wzs 我已经添加了更多的解释。 - John1024

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接