2010-04-26 2 views
2

У меня есть скрипт bash, который хочет выполнить некоторую работу параллельно, я сделал это, поставив каждое задание в подоболочку, которая запускается в фоновом режиме. В то время как количество заданий, выполняемых одновременно, должно быть ограничено, я достигаю этого, сначала поместив некоторые строки в FIFO, а затем перед тем, как развернуть подоболочку, родительский скрипт должен прочитать строку из этого FIFO. Только после того, как он получит строку, она может разблокировать подоболочку. До сих пор все работает нормально. Но когда я попытался прочитать строку из FIFO в подоболочке, кажется, что только одна подоболочка может получить строку, даже если в FIFO есть больше строк. Поэтому я удивляюсь, почему другие подоболочки (s) не могут читать строку, даже если в FIFO больше строк.
Мой код тестирования выглядит примерно так:
Данные в linux 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

ответ

0

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

+0

Спасибо, но это не имеет значения. Кстати, мой bash имеет следующую информацию о версии: GNU bash, версия 3.00.15 (1) -release (x86_64-redhat-linux-gnu) Copyright (C) 2004 Free Software Foundation, Inc. – Utoah

+0

@Utoah: Я пробовал его в 3.2 и 4.0, и он работал в обоих. –

0

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

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

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

0

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

После завершения этого кода, команда «кошка /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сок, команда «кошка /tmp/fy_u_test2.fifo» дает следующий результат:

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

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

+0

Я предполагаю, что это проблема. –

0

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

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

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

+0

Звучит разумно, но он не может объяснить симптом в моем почтовом отправлении, потому что, когда подоболочка раздваивается, он получает копию FIFO fd как для чтения, так и для записи. Таким образом, до тех пор, пока существует работающая подоболочка, FIFO имеет как читателя, так и автора. – Utoah

+0

У меня еще вопрос, когда данные в FIFO отбрасываются ядром. Это когда последний читатель FIFO закрывает свой конец чтения, что ядро ​​отбрасывает все оставшиеся данные в нем? Благодарю. – Utoah

+0

IIRC, первый раз, когда он закрыт, вот и все. POSIX оставляет открытие открытого FIFO для чтения/записи неопределенным (см. Http://linux.die.net/man/7/fifo), и на странице говорится, что следует использовать осторожность при открытии для чтения и записи под Linux, в противном случае блокировка может возникнуть. Если бы я делал то, что вы делаете с FIFO, я бы использовал один процесс для записи, один процесс для чтения и связь между процессами с использованием другого механизма для отправки/получения данных. Я не вижу ничего, позволяя нескольким процессам безопасно совместно использовать FIFO (или нормальный канал, если на то пошло). –

1

Кажется, что-то связано с вызовом оболочки «read -u6». Если у меня STDIN оболочки закрыта, когда выдается «read -u6», она пытается прочитать 128 байт из fd 6. Но если STDIN остается нетронутым, когда выдается «read -u6», он считывает байты один на один до тех пор, пока не встретится «\ n». Я обнаружил это странное действие от «Трассирования», где в первом случае «читать -u6» звонок вызвал следующий системный вызов:

read(6, "0\n1\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n"..., 128) = 50 

и в последнем случае «читать -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 
0

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

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

Вот способ получить то, что вы хотите с помощью простого файла в качестве своего рода очереди:

#!/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: Этот метод не подходит для неограниченной работы, поскольку он добавляет байты в файл стек для каждый процесс, который он вызывает, означает, что файл стека растет медленно.