Данные в Linux FIFO кажутся потерянными - PullRequest
2 голосов
/ 26 апреля 2010

У меня есть скрипт bash, который хочет выполнять некоторую работу параллельно, я делал это, помещая каждое задание в подоболочку, которая запускается в фоновом режиме. Хотя число одновременно выполняемых заданий должно быть ограничено, я достигаю это, сначала помещая несколько строк в FIFO, а затем непосредственно перед разветвлением подоболочки, родительский скрипт должен прочитать строку из этого FIFO. Только после того, как он получит строку, он может раскошелиться на подоболочку. До сих пор все отлично работает. Но когда я попытался прочитать строку из FIFO в подоболочке, оказалось, что только одна подоболочка может получить строку, даже если в FIFO явно больше строк. Поэтому мне интересно, почему другие подоболочки не могут читать строки, даже если в 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=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 блокируется там, пока я не выдам:
что-то повторить> /tmp/fy_u_test2.fifo

Ответы [ 7 ]

1 голос
/ 28 апреля 2010

Кажется, что-то связано с вызовом оболочки 'read -u6'. Если у меня есть STDIN закрытой оболочки, когда выдается 'read -u6', он пытается прочитать 128 байтов из fd 6. Но если 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
1 голос
/ 26 апреля 2010

Имейте в виду, что FIFO в системах POSIX по сути является именованным каналом. Чтобы перемещать данные по каналу, одной стороне нужен считыватель, а другой стороне - устройство записи, а когда одна закрыта, другая теряет полезность.

Другими словами, вы не можете cat на fifo после выхода какого-либо другого считывателя, потому что содержимое FIFO исчезнет.

Возможно, вы захотите узнать об использовании обычного файла (и использовать блокировку файлов, чтобы обеспечить синхронизацию доступа к этому обычному файлу), или использовать каталог с несколькими файлами в нем, или даже использовать общую память или что-то подобное к этому (возможно, не в сценарии оболочки, хотя). Все зависит от того, какова ваша конечная цель, на самом деле, каким будет наилучший путь для достижения этой цели.

1 голос
/ 26 апреля 2010

Возможно ли, что в вашей записи в fifo происходит некоторая буферизация? Если у вас есть доступ к небуферу, не могли бы вы попытаться с этим справиться? Я действительно не понимаю, как это могло произойти здесь, но симптомы подходят так, что стоит попробовать.

0 голосов
/ 05 августа 2010

По причинам, объясненным в других ответах здесь, вам не нужен канал, если вы не можете читать и писать из канала одновременно.

Поэтому целесообразно использовать другое средство 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"

Sidenote: этот метод не идеален для неограниченной работы, поскольку он добавляет байт в файл стека для каждого вызываемого им процесса, что означает, что файл стека растет медленно.

0 голосов
/ 26 апреля 2010

Я обнаружил, что данные остались непрочитанными в 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
0 голосов
/ 26 апреля 2010

Это может быть проблема параллелизма, когда обе субшарики пытаются читать из одного и того же fifo в одно и то же время.Это случается все время?

Вы можете попробовать добавить оператор flock -x 6 или изменить задержку для двух подоболочек и посмотреть, что произойдет.

Кстати, я могу подтвердить, что с помощью bash 3.2 и ядра 2.6.28 ваш кодотлично работает.

0 голосов
/ 26 апреля 2010

Я получаю все четыре строки в файле журнала при запуске. Что произойдет, если вы измените свой шебанг на #!/bin/bash?

...