Возможное состояние гонки с передачей по конвейеру от нескольких получателей-тройников, поступающих вне последовательности по именованному каналу в сценарии BASH - PullRequest
5 голосов
/ 10 марта 2012

ОБНОВЛЕНИЕ: Хотя на самом деле я не решал исходную проблему, связанную с моими усилиями в области трубопроводов, я решил свою проблему, значительно упростив ее и просто отказавшись от труб. Вот сценарий проверки концепции, который параллельно генерирует при чтении только один раз с диска контрольные суммы CRC32, MD5, SHA1, SHA224, SHA256, SHA384 и SHA512 и возвращает их в виде объекта JSON (будет использовать выходные данные в PHP) и рубин). Это сыро без проверки ошибок, но работает:

#!/bin/bash

checksums="`tee <"$1" \
        >( cfv -C -q -t sfv -f - - | tail -n 1 | sed -e 's/^.* \([a-fA-F0-9]\{8\}\)$/"crc32":"\1"/' ) \
        >( md5sum - | sed -e 's/^\([a-fA-F0-9]\{32\}\) .*$/"md5":"\1"/' ) \
        >( sha1sum - | sed -e 's/^\([a-fA-F0-9]\{40\}\) .*$/"sha1":"\1"/' ) \
        >( sha224sum - | sed -e 's/^\([a-fA-F0-9]\{56\}\) .*$/"sha224":"\1"/' ) \
        >( sha256sum - | sed -e 's/^\([a-fA-F0-9]\{64\}\) .*$/"sha256":"\1"/' ) \
        >( sha384sum - | sed -e 's/^\([a-fA-F0-9]\{96\}\) .*$/"sha384":"\1"/' ) \
        >( sha512sum - | sed -e 's/^\([a-fA-F0-9]\{128\}\) .*$/"sha512":"\1"/') \
        >/dev/null`\ 
"

json="{"

for checksum in $checksums; do json="$json$checksum,"; done

echo "${json:0: -1}}"

ОРИГИНАЛЬНЫЙ ВОПРОС:

Я немного боюсь задать этот вопрос, так как у меня было так много совпадений с моей поисковой фразой, что после применения знаний, собранных из Использование именованных каналов с bash - Проблема с потерей данных и чтение еще через 20 страниц я все еще нахожусь в тупике с этим.

Итак, чтобы продолжить, я делаю простой сценарий, который позволяет мне одновременно создавать контрольные суммы CRC32, MD5 и SHA1 для файла, одновременно считывая его с диска только один раз. Я использую cfv для этой цели.

Первоначально, я просто взломал простой скрипт, который написал cat'ted файл для тройки с тремя командами cfv, записывающими в три отдельных файла в / tmp /, а затем попытался отследить их в stdout, но потом закончил с пустой вывод, если я не заставил свой сценарий поспать секунду, прежде чем пытаться прочитать файлы. Думая, что это странно, я предположил, что я был дебилом в своих сценариях, поэтому я попытался использовать другой подход, вместо этого выводя работников cfv в именованный канал. Пока что это мой сценарий, после применения методов из вышеупомянутой ссылки:

!/bin/bash

# Bail out if argument isn't a file:
[ ! -f "$1" ] && echo "'$1' is not a file!" && exit 1

# Choose a name for a pipe to stuff with CFV output:
pipe="/tmp/pipe.chksms"

# Don't leave an orphaned pipe on exiting or being terminated:
trap "rm -f $pipe; exit" EXIT TERM

# Create the pipe (except if it already exists (e.g. SIGKILL'ed b4)):
[ -p "$pipe" ] || mkfifo $pipe

# Start a background process that reads from the pipe and echoes what it
# receives to stdout (notice the pipe is attached last, at done):
while true; do
        while read line; do
                [ "$line" = "EOP" ] && echo "quitting now" && exit 0
                echo "$line"
        done
done <$pipe 3>$pipe & # This 3> business is to make sure there's always
                      # at least one producer attached to the pipe (the
                      # consumer loop itself) until we're done.

# This sort of works without "hacks", but tail errors out when the pipe is
# killed, naturally, and script seems to "hang" until I press enter after,
# which I believe is actually EOF to tail, so it's no solution anyway:
#tail -f $pipe &

tee <"$1" >( cfv -C -t sfv -f - - >$pipe ) >( cfv -C -t sha1 -f - - >$pipe ) >( cfv -C -t md5 -f - - >$pipe ) >/dev/null

#sleep 1s
echo "EOP" >$pipe
exit

Итак, выполнив все как есть, я получаю следующий вывод:

daniel@lnxsrv:~/tisso$ ./multisfv file
 :  :  : quitting now
- : Broken pipe (CF)
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
- : Broken pipe (CF)
close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr
- : Broken pipe (CF)
daniel@lnxsrv:~/tisso$ close failed in file object destructor:
sys.excepthook is missing
lost sys.stderr

Но с закомментированным сном 1 я получаю ожидаемый результат,

daniel@lnxsrv:~/tisso$ ./multisfv file
3bc1b5ff125e03fb35491e7d67014a3e *
-: 1 files, 1 OK.  0.013 seconds, 79311.7K/s
5e3bb0e3ec410a8d8e14fef1a6daababfc48c7ce *
-: 1 files, 1 OK.  0.016 seconds, 62455.0K/s
; Generated by cfv v1.18.3 on 2012-03-09 at 23:45.23
;
2a0feb38
-: 1 files, 1 OK.  0.051 seconds, 20012.9K/s
quitting now

Это озадачивает меня, так как я предполагаю, что tee не выйдет до тех пор, пока не завершится работа каждого получателя cfv, к которому он подключен, и, таким образом, оператор echo "EOP" будет выполняться, пока не завершатся все подпотоки cfv, что будет они бы записали свой вывод в мой именованный канал ... И тогда выполнил бы оператор echo.

Поскольку поведение без конвейеров такое же, только с использованием временных выходных файлов, я думаю, что это должно быть какое-то условие гонки, связанное с тем, как тройник передает данные своим получателям? Я попробовал простую команду «подождать», но она, конечно, будет ждать завершения моего дочернего процесса bash - цикла while - поэтому я просто получаю зависание процесса.

Есть идеи?

ТИА, Даниил:)

1 Ответ

2 голосов
/ 14 сентября 2012

tee выйдет, как только он запишет последний бит ввода в последний выходной канал и закроет его (то есть, неназванные каналы, созданные bash, а не fifo, или «именованный канал»).Не нужно ждать окончания процессов, считывающих каналы;на самом деле, он даже не подозревает, что пишет даже в каналы.Поскольку каналы имеют буферы, вполне вероятно, что tee завершает запись до того, как процессы на другом конце завершат чтение.Таким образом, сценарий запишет 'EOP' в fifo, что приведет к прекращению цикла чтения.Это закроет единственный читатель fifo, и все процессы cfv получат SIGPIPE, когда они в следующий раз попытаются записать в stdout.

Очевидный вопрос, который нужно здесь задать, заключается в том, почему вы не просто запускаете три (или N)независимые процессы, читающие файл и вычисляющие различные резюме.Если «файл» фактически создавался на лету или загружался с какого-либо удаленного сайта, или с какого-либо другого медленного процесса, возможно, имеет смысл делать вещи так, как вы пытаетесь это сделать, но если файл присутствует на локальном компьютере.диск, вполне вероятно, что на самом деле произойдет только один доступ к диску;запаздывающие сумматоры будут читать файл из буферного кеша.Если это все, что вам нужно, параллельная GNU должна работать нормально, или вы можете просто запустить процессы в bash (с помощью &) и затем ждать их.YMMV, но я думаю, что любое из этих решений будет менее ресурсоемким, чем настройка всех этих каналов и моделирование буферного кэша в пользовательском пространстве с помощью tee.

Кстати, если вы хотите сериализовать вывод из несколькихпроцессы, вы можете использовать утилиту flock.Простого использования fifo недостаточно;нет никакой гарантии, что процессы, пишущие в fifo, будут записывать целые строки атомарно, и если бы вы знали, что они это сделали, вам не понадобится fifo.

...