LINUX.ORG.RU

Мистическая история про линукс, трубы, и мёртвых котят

 , , , ,


0

2

Добрый день. По какой причине сообщение может не доходить до получателя?

Как воспроизвести.

Выполняем в окне терминала

mkfifo mypipe
exec 3<>mypipe
while read -rt3 ss; do echo "${ss}"; done < <(cat mypipe)

в соседнем окне поочерёдно вводим что-то вроде такого (нужно успеть вводить в течение 3 секунд)

echo 'as1' >mypipe
echo 'as2' >mypipe
echo 'as1' >mypipe
echo 'as2' >mypipe
echo 'as1' >mypipe
echo 'as2' >mypipe

получаем

as1
as2
as1
as1
as2

или

as1
as1
as2
as1
as2

или не получаем, это работает не каждый раз.

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

В интернете говорят, что нельзя узнать степень заполненности трубы нормально, возможно удалось бы что-нибудь отследить. Отправлять ответ, что, мол, получили и прочитали? И если нет, то посылать ещё раз?

Может быть, избавиться от exec? Я пока не могу придумать как. Вот совсем не получается, блокируются и отправитель и получатель.

И, кроме того, в неинтерактивной сессии cat остаётся висеть в памяти вместе со скриптом, мне приходится писать kill cat при завершении, потому что её некому остановить. Как быть с этим? Я не могу закрыть трубу или сделать так, чтобы кошка отвалилась?

Справедливости ради, на практике мне первая проблема не встречалась. Что это, магия интерактивной сессии, или просто сложнее заметить?



Последнее исправление: linuxnewbie (всего исправлений: 3)

По поводу подтверждения, это же придётся как-то подтверждать и синхронизировать и эти подтверждения. Отдельной трубой? В однострочник не уложимся.

linuxnewbie
() автор топика

Окей, можно написать что-то вроде exec 3>&- && exec 3<&-, но в таком случае при неудачном стечении обстоятельств возникает состояние гонки и процесс который пишет в трубу зависает.

Это именно то, чего я пытаюсь тут избежать. Да и в целом я не против убивать кошек, только это тоже чревато. Например, я убью чужую кошку.

Проблему номер раз я всё ещё не могу понять. Какая-то буферизация? Где? Как с ней работать? Непонятно...

linuxnewbie
() автор топика
Ответ на: комментарий от anonymous

Мне нравится баш. Он быстро работает и не просит много памяти. И много чего умеет полезного. Да и в любом случае, шелл вызывать придётся, как ни пиши.

linuxnewbie
() автор топика
Ответ на: комментарий от anonymous

Ну хорошо, достаточно быстро работает и быстро запускается. Сравниваю с альтернативами в лице zsh и python. Первый хорош для интерактивного скриптинга, второй для всего остального.

Давайте лучше обсудим мою проблему. Я убиваю детей с pkill и всё хорошо и приятно получается, только всё равно остаётся висеть (хотя бы шел завершается). Я даже перезакрываю дескриптор, всё одно. Но получилось избавиться от моих котят опять же с pkill, он может матчить полную команду. Они успешно умирают, даже всем скопом. Хотя это не корректное завершение. Закрыть дескриптор и послать последнее сообшение, чтобы закрылась сама? Как-то не то.

А вот что делать с тем, что сообщения могут случайным образом в трубе исчезать я не знаю. Мне необходимо хотя бы понять в чём дело.

linuxnewbie
() автор топика
Ответ на: комментарий от linuxnewbie

Кстати странно, интерактивная кошка отваливается когда я дескриптор закрываю. Что не так?

linuxnewbie
() автор топика

Вообще странно что у вас оно хоть както работает, так как не должно.

При выполнении команды «в соседнем окне»

echo 'as1' >mypipe
Для mypipe будет вызван open -> write -> close

И «close» увидит «cat mypipe» в основном скрипте,

exec 3<>mypipe
Вообще не понятно зачем сдесь нужно, скорее всего этот дескриптор и приводит к рандомному поведению.

zaz ★★★★
()
Ответ на: комментарий от zaz

Это продвинутые техники скриптинга, всё нормально. Я читаю построчно выхлоп трубы, с блокировкой пока не прочитается. Труба аккумулирует всё что в неё запишут, построчно.

В интерактивной сессии кошка отваливается, если закрыть дескриптор. И теряются данные — они уходят в трубу, но не выходят из неё. В скрипте кошка остаётся в подвешенном состоянии, но данные пока не пропадали. «Пока» это около полусотни запусков, глубина «очереди» 2-10.

linuxnewbie
() автор топика

Строка пропадает, потому что её считывает экземпляр cat, подвисший с предыдущего запуска этой команды.

while read -rt3 ss; do echo "${ss}"; done < <(cat mypipe)

Если делать так, ничего не пропадает:

while read -rt3 ss; do echo "${ss}"; done <&3
unterwulf
()

Так, в интерактивной сессии zsh ничего не теряется. Но кошка не отваливается когда риду прилетает таймаутом по голове. Выглядит это поведение как у неинтерактивного баша. Интересненько.

linuxnewbie
() автор топика
Ответ на: комментарий от unterwulf

Во, спасибо. У меня почему-то не получилось тогда с этим дескриптором, пришлось кошек городить. Так всё работает и в bash и в zsh.

linuxnewbie
() автор топика
Ответ на: комментарий от linuxnewbie

Это можно отладить в bashdb? Как лучше к нему подступиться?

linuxnewbie
() автор топика
Ответ на: комментарий от linuxnewbie

Как лучше к нему подступиться?

В выводе ps видно, что cat жив. Видимо bash его не слишком рьяно пытается прибить, и поэтому он, будучи заблокированным на чтении из пайпа, висит и не завершается. Другие шеллы смотреть не интересно, но, думаю strace-ом вполне можно понять, что там происходит, не глядя в исходники. Может они SIGKILL используют, например.

unterwulf
()
Ответ на: комментарий от unterwulf

Конечно, он жив и блокируется. И read построчно считывает из него всё то что он получает из трубы (в другую трубу), к которой он подключен постоянно. Проблема в том, что при чтении из трубы там может не оказаться данных, и я не могу понять с чем это связано, и почему это проявляется только в интерактивной сессии баша.

strace

Поведение под strace тоже отличается, оно как у интерактивного zsh и неинтерактивного баша и совершенно логичное. Кроме того, ничего не теряется. А bashdb с однострочником не хочет работать.

write(1, "as1\n", 4)                    = 4
read(4, "as2\n", 131072)                = 4
write(1, "as2\n", 4)                    = 4
read(4, "as1\n", 131072)                = 4
write(1, "as1\n", 4)                    = 4
read(4, "as2\n", 131072)                = 4
write(1, "as2\n", 4)                    = 4
read(4, "as1\n", 131072)                = 4
write(1, "as1\n", 4)                    = 4
read(4, "as2\n", 131072)                = 4
write(1, "as2\n", 4)                    = 4
read(4, "as8788\n", 131072)             = 7
write(1, "as8788\n", 7)                 = -1 EPIPE (Обрыв канала)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=14399, si_uid=1000} ---
+++ killed by SIGPIPE +++
linuxnewbie
() автор топика
Ответ на: комментарий от linuxnewbie

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

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

И вообще, для этого эксперимента, важно не какая сессия выполняется сейчас, а какая выполнялась последней перед текущей. Т.к. чтобы наблюдалась потеря, cat-вредитель уже должен быть порождён.

unterwulf
()
Ответ на: комментарий от unterwulf

А, кажется теперь вижу, да. Заметно, если запустить их несколько – они ждут, пока прилетит SIGPIPE при попытке записать (в read).

Тогда понятно, почему. В скрипте каждый запуск порождает свою трубу, не связанную с предыдущей. Фантомных чтений просто не случается.

Но вопрос остаётся, почему баш выдаёт приглашение, оставляя cat висеть и ждать пока ему что-нибудь придёт, чтобы тому отвалиться? И делает это только когда запушен не под strace, т.е. под strace он работает корректно? Мне было не видно в интерактивной сессии, и в отличие от скрипта cat успешно умирает от SIGHUP при закрытии сессии, поэтому его было не найти.

Zsh не прерывает соединение, даже когда у read наступает таймер и ждёт последнее сообщение (которое ему некуда записать). Значит ли это, что из zsh получается bash лучше, чем из bash? Или что в bash очень много багов, проявляющихся только пока их не пытаешься отлаживать? И их нет в zsh? Ну всё, меняю дефолтный шелл на zsh и удаляю bash.

linuxnewbie
() автор топика

на кой там cat, если уже есть exec?

while read -rt3 -u3 ss; do echo "${ss}"; done

и будет тебе счастье

ananas ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.