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

时间:2014-10-13 16:16:38

标签: bash fifo

我写了一个这样的脚本:

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

没关系。

当我直接使用FIFO时,它无法连续从FIFO中读取数据,它会挂起。当我使用文件描述符时,它没关系。是什么原因?

1 个答案:

答案 0 :(得分:3)

替换它:

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,则任何未读信息都将丢失。

  3. 这意味着您的上述脚本的行为方式可能取决于计时事故。当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"
    

    在等待进程开始读取FIFO时,上述脚本将在第一个echo期间挂起。因为它挂起,所以永远不会到达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语句。