linux FIFO中的数据似乎丢失了

linux FIFO中的数据似乎丢失了,linux,bash,shell,fifo,Linux,Bash,Shell,Fifo,我有一个bash脚本,它想并行地做一些工作,我把每个工作放在后台运行的子shell中。虽然同时运行的作业的数量应该在一定的限制下,但我通过首先在FIFO中放入一些行来实现这一点,然后在分叉子shell之前,需要父脚本从FIFO中读取一行。只有当它得到一条线后,它才能进入地下室。到目前为止,一切正常。但是当我试图在子shell中读取FIFO中的一行时,似乎只有一个子shell可以获得一行,即使FIFO中显然有更多的行。所以我想知道为什么其他子shell不能读取一行,即使FIFO中有更多行。 我的

我有一个bash脚本,它想并行地做一些工作,我把每个工作放在后台运行的子shell中。虽然同时运行的作业的数量应该在一定的限制下,但我通过首先在FIFO中放入一些行来实现这一点,然后在分叉子shell之前,需要父脚本从FIFO中读取一行。只有当它得到一条线后,它才能进入地下室。到目前为止,一切正常。但是当我试图在子shell中读取FIFO中的一行时,似乎只有一个子shell可以获得一行,即使FIFO中显然有更多的行。所以我想知道为什么其他子shell不能读取一行,即使FIFO中有更多行。
我的测试代码如下所示:

在这里,我希望有这样一句话:


 32561 This is in child # 2, read --- 3 --- from 6 \n
但它从未出现,并且在我发出以下命令之前,child#2进程在那里被阻止:

echo something>/tmp/fy___test2.fifo

当我运行日志文件时,我得到了日志文件中的所有四行。如果将shebang更改为
#,会发生什么/bin/bash

这可能是一个并发问题,两个子shell试图同时从同一fifo读取数据。这种事经常发生吗

您可以尝试添加
flock-x6
语句,或者更改两个子shell的延迟,看看会发生什么


顺便说一句,我可以确认,在bash 3.2和内核2.6.28中,您的代码运行良好。

当父shell退出时丢失了父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

#!/垃圾箱/垃圾箱
fifo_path=“/tmp/fy__测试2.fifo”
mkfifo$fifo_路径
#打开fd 6处r/w的fifo
exec 6$fifo_路径
进程数=9
#将$process_num行放入FIFO
对于((i=0;i&6
我在12-3;
做
read-u6
完成
此代码结束后,命令“cat/tmp/fy____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

#!/bin/sh
fifo_path=“/tmp/fy__测试2.fifo”
mkfifo$fifo_路径
#打开fd 6处r/w的fifo
exec 6$fifo_路径
进程数=9
#将$process_num行放入FIFO
对于((i=0;i&6
我在12-3;
做
read-u6
完成
#__注意这一行__
睡60
发出此代码运行后,在休眠60秒期间,命令“cat/tmp/fy___test2.fifo”给出以下输出:

$ cat /tmp/fy_u_test2.fifo 3 4 5 6 7 8 $cat/tmp/fy___测试2.fifo 3. 4. 5. 6. 7. 8.
是否可能在写入fifo时存在缓冲?如果您有可用的解除缓冲,您可以尝试用它来前置回声吗?我真的不知道它是如何发生的,但症状适合,因此值得一试。

请记住,POSIX系统上的fifo本质上是一个命名管道。为了移动数据在管道上,一边需要一个读者,另一边需要一个作家,当一个关闭时,另一个就失去了用处

换句话说,在其他读卡器退出后,不能对fifo执行
cat
,因为fifo的内容将消失


您可能希望了解如何使用普通文件(并使用文件锁定来确保您正在同步对该普通文件的访问),或者使用包含多个文件的目录,甚至使用共享内存或类似的内容(不过,可能不在shell脚本中)。这一切都取决于你的最终目标是什么,实际上,最好的方法是什么。

似乎与“read-u6”shell调用有关。如果我关闭了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
30371 16:27:15 read(6, "0", 1)          = 1
30371 16:27:15 read(6, "\n", 1)         = 1
在后一种情况下,“read-u6”调用导致以下系统调用:

read(6, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n"..., 128) = 50
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

#!/bin/bash
fifo_path=“/tmp/fy__测试2.fifo”
mkfifo$fifo_路径
#打开fd 6处r/w的fifo
exec 6$fifo_路径
#注释或取消注释下面的行会有所不同
执行0>&-
进程数=20
#将$process_num行放入FIFO
对于((i=0;i&6
耽搁{
本地索引=“$1”
echo“这是您可以看到的。$index\n”
睡眠10;
}
#在每次迭代中,尝试从FIFO读取两行,一行来自此shell,
#另一个来自地下室
因为我在123
做
日期>>/tmp/fy\U日期
#如果可以从FIFO读取一行,则在bk中运行子shell,否则,阻塞。
read-u6
echo“$$Read---$REPLY---from 6\n”>>/tmp/fy_date
{
耽误你一点钱
#尝试从FIFO读取一行
#read-u6
echo“$$This is in child#$i,read---$REPLY---from 6\n”>/tmp/fy_date
echo“$$这是在child#$i中,读取---$REPLY---from 6\n”>/tmp/fy_date
回显“$i xx”>&6
#回声xx>&6
} &
完成
#睡眠13
#等等
#read-u6
echo“$$在fork之后,在parent中,读取---$REPLY---from 6\n”>>/tmp/fy\u date

由于其他答案中解释的原因,您不想要管道,除非您可以同时从管道读写

因此,建议使用IPC的另一种方式,或重新调整FIFO的使用方式,以便在主进程创建工作进程(或相反)时,异步进程填满管道

以下是一种使用简单文件作为队列获取所需内容的方法:

#!/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"
!/usr/bin/env bash
stack=/tmp/stack
>“$stack”
#在堆栈上创建最初的5个点
因为{1..5}中的i;do
echo>>“$stack”
完成
因为{1..10}中的i;do
#等待堆栈上的一个点。
直到阅读;睡觉1;完成
{
回显“启动进程#$i”
睡眠$((5+i))#做些有成效的事情
回音“结束进程#$i”
#我们完成了,释放我们在堆栈上的位置。
echo>>“$stack”
} &
完成<“$stack”
旁注:此方法不适合无限工作,因为它为调用的每个进程向堆栈文件添加一个字节,这意味着堆栈文件增长缓慢。