linux FIFO中的数据似乎丢失了

时间:2010-04-26 08:45:15

标签: linux bash shell fifo

我有一个bash脚本想要并行完成一些工作,我是通过将每个作业放在后台运行的子shell中来完成的。虽然同时运行的作业数量应该在一定限度内,但我首先在FIFO中放入一些行来实现这一点,然后在分配子shell之前,父脚本需要从该FIFO读取一行。只有在获得一行后才能分叉子shell。到目前为止,一切正常。但是当我尝试从子shell中读取FIFO中的一行时,似乎只有一个子shell可以获得一条线,即使FIFO中有明显更多的线。所以我想知道为什么即使FIFO中有更多行,其他子shell也无法读取一行。
我的测试代码看起来像这样:


#!/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进程在那里被阻止,直到我发出:
回声一下&gt; /tmp/fy_u_test2.fifo

7 个答案:

答案 0 :(得分:1)

你对fifo的写入是否有可能进行缓冲?如果你有unbuffer可用,你可以尝试用回声前面的回声吗?我真的不知道它会如何发生,但症状适合所以值得一试。

答案 1 :(得分:1)

请记住,POSIX系统上的FIFO本质上是一个命名管道。为了在管道上移动数据,一方需要一个读者,另一方需要一个写入器,当一方关闭时另一方失去实用性。

换句话说,在其他读者退出之后,你不能在{fif}上cat,因为FIFO的内容将会消失。

您可能希望看到使用普通文件(并使用文件锁定来确保您正在同步对该普通文件的访问),或者使用包含多个文件的目录,甚至使用共享内存或类似文件为此(也许不是在shell脚本中)。这完全取决于你的最终目标是什么,真正的,最好的方法是什么。

答案 2 :(得分:1)

似乎与'read -u6'hell调用有关。如果我关闭了shell的STDIN,当发出'read -u6'时,它会尝试从fd 6读取128个字节。但如果STDIN保持不变,当发出'read -u6'时,它会读取一个字节一直到遇到'\ n'。我从'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

答案 3 :(得分:0)

运行时,我在日志文件中获取了所有四行。如果您将shebang更改为#!/bin/bash会怎样?

答案 4 :(得分:0)

这可能是一个并发问题,两个子shell都试图同时从同一个fifo读取。它会一直发生吗?

您可以尝试添加flock -x 6语句或更改两个子广告的延迟,看看会发生什么。

BTW,我可以确认使用bash 3.2和内核2.6.28,你的代码工作正常。

答案 5 :(得分:0)

当父shell退出时,我发现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'给出以下输出:

$ cat /tmp/fy_u_test2.fifo 
3
4
5
6
7
8

答案 6 :(得分: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"

旁注:这种方法对于无限制的工作并不理想,因为它为堆栈文件添加一个字节,用于它调用的每个进程,这意味着堆栈文件缓慢增长。