我写了一个这样的脚本:
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中读取数据,它会挂起。当我使用文件描述符时,它没关系。是什么原因?
答案 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非常棘手:
尝试写入FIFO将被阻止,除非另一个进程已准备好从FIFO读取。
如果从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"
在等待进程开始读取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
语句。