Межпроцессное взаимодействие без FIFO

Внутри сценария BASH мы можем иметь несколько процессов, работающих в фоновом режиме, которые взаимодействуют друг с другом с помощью именованных каналов, FIFO, зарегистрированных в файловой системе. Примером этого может быть:

#!/bin/bash
mkfifo FIFO

# BG process 1
while :; do echo x; done & >FIFO

# BG process 2
while :; do read; done & <FIFO

exit

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


person davide    schedule 02.06.2012    source источник
comment
Вы можете использовать mktemp, чтобы придумать уникальное имя   -  person Alexandre C.    schedule 02.06.2012
comment
Я бы предпочел вообще не управлять файловой системой. Кроме того, при быстром создании / удалении файлов или FIFO взаимодействие с файловой системой снижает производительность.   -  person davide    schedule 02.06.2012
comment
К сожалению, оболочка Bourne не очень гибко справляется с этими вещами. IPC определенно возможен без FIFO, поскольку они появились довольно недавно: основные вызовы - это socketpair / pipe.   -  person Nicholas Wilson    schedule 02.06.2012
comment
@NicholasWilson: Звучит интересно. Не могли бы вы указать мне ссылку на документ или привести очень краткий пример?   -  person davide    schedule 02.06.2012
comment
Извините, там неуместная формулировка. То, что я пытался сказать: FIFO появились недавно, поэтому IPC явно возможен без них. Если вы не используете FIFO, основные вызовы будут связаны с парой сокетов (или конвейером). Bash имеет множество забавных ограничений для файловых дескрипторов, поэтому обычно лучше сразу перейти к чему-то вроде python или (я предпочитаю) оболочке C, которая выполняет вызовы, которые вы хотите выполнить напрямую.   -  person Nicholas Wilson    schedule 02.06.2012


Ответы (5)


Вот пример, который запускает два подпроцесса, реализованных как функции одного и того же сценария оболочки ... Один подпроцесс генерирует числа 1 ... 5 (засыпает между отпечатками), второй читает из фиксированного файлового дескриптора (5, в который STDOUT из первый FD перенаправляется на), умножается на 2 и снова печатается. Основной процесс перенаправляет STDOUT этого второго процесса на другой фиксированный файловый дескриптор (6), а затем читает из этого в цикле.

Он работает в основном так же, как и в C-коде, с парами fd, созданными системным вызовом pipe (2). Чтобы понять, что происходит, запустите сценарий под strace -f!

Версия Bash - 4.2.24 (1), работающая на Ubuntu / x86.

[ubuntu /home/chris]
$ bash --version
GNU bash, version 4.2.24(1)-release (i686-pc-linux-gnu)
Copyright (C) 2011 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>

This is free software; you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

Вывод скрипта:

[ubuntu /home/chris]
$ ./read_from_fd.sh
Got number 2.
Got number 4.
Got number 6.
Got number 8.
Got number 10.

Исходный код:

#!/bin/bash

# Generate data, output to STDOUT.
generate_five_numbers() {
        for n in `seq 5` ; do
                echo $n
                sleep 2
        done
}

# Read data from FD#5, multiply by two, output to STDOUT.
multiply_number_from_fd5_by_two() {
        while read n <&5 ; do
                echo "$(( $n * 2 ))"
        done
}

# choose your FD number wisely ;-)

# run generator with its output dup'ed to FD #5
exec 5< <( generate_five_numbers )

# run multiplyier (reading from fd 5) with output dup'ed to FD #6
exec 6< <( multiply_number_from_fd5_by_two )

# read numbers from fd 6
while read n <&6 ; do
        echo "Got number $n."
done

Дерево процессов во время работы:

──read_from_fd.sh(8118)─┬─read_from_fd.sh(8119)───sleep(8123)
                        └─read_from_fd.sh(8120)
person Christian Vogel    schedule 07.12.2012

Bash 4 имеет сопроцессы.

Вы также можете использовать анонимные именованные каналы, также известные как подстановка процесса в Bash. 2, 3 или 4.

person Dennis Williamson    schedule 02.06.2012
comment
Поправьте меня, если я ошибаюсь, но я думаю, что таким образом вы не сможете перенаправить оба STDIN и STDOUT процесса одновременно. Кроме того, Bash поддерживает только один сопроцесс за один процесс; что, если у нас есть много фоновых процессов, которым необходимо взаимодействие? - person davide; 02.06.2012
comment
@davide: Тогда я бы посоветовал вам использовать что-нибудь кроме Bash. Между прочим, в справочном руководстве говорится, что между исполняющейся оболочкой и сопроцессом устанавливается двусторонний канал [выделено мной]. - person Dennis Williamson; 02.06.2012
comment
Извини, Деннис, я не понял. Я имею в виду, что я не уверен, возможно ли двустороннее перенаправление с заменой процесса. Конечно coproc может, но, к сожалению, Bash ограничивает его использование только одним экземпляром. - person davide; 02.06.2012

Вы можете использовать nc (также известный как netcat), который позволяет подключать стандартные потоки скрипта к сети. разъем. Конечно, он также работает на localhost, поэтому вы можете использовать его для IPC между скриптами. Бонусом является возможность запускать скрипты на разных хостах, что невозможно с FIFO (хорошо, возможно, на NFS это так, но это было бы довольно громоздко для настройки, если у вас еще нет NFS).

person Michał Kosmulski    schedule 04.06.2012
comment
Да, это тоже сработает, но разве мне (или конечному пользователю) не потребуется обращать внимание на то, чтобы не использовать сетевые порты (их могут быть сотни), уже занятые другими службами, работающими на том же компьютере? - person davide; 04.06.2012
comment
Возможно, да, но если вы выберете случайный порт из безопасного диапазона (не временный порт) это маловероятно. Кроме того, если вы скажете nc прослушивать порт, который уже занят, он немедленно вернется с ошибкой, после чего вы можете попробовать следующий порт и так далее. Конечно, тогда вам придется передать порт другому приложению через другой канал, например, через файл. Однако, вероятно, вам понадобится это только один раз на конкретной машине. Любое решение должно работать достаточно хорошо, чтобы быть практичным. - person Michał Kosmulski; 05.06.2012
comment
Кроме того, этот способ связи могут усложнить настройки брандмауэра. - person James M. Lay; 23.12.2016

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

Часть, которая получает данные:

node -e "require('net').createServer(function(s){s.pipe(process.stdout)}).listen(1337)"

Часть, которая отправляет данные:

echo "write clean code they said" > /dev/tcp/localhost/1337
echo "it will pay off they said" > /dev/tcp/localhost/1337

К моему удивлению, работает даже в Bash от MSysGit для Windows.

person Camilo Martin    schedule 03.02.2016

Вы рассматривали возможность использования сигналов? Если единственное, что вам нужно, это вызвать событие (без передачи аргументов), использование kill и trap работает отлично (однако будьте осторожны с семантикой, используйте, например, SIGUSR1).

Однако вам может потребоваться переработать логику, как в примере ниже:

subprocess_finished()
{
    np=$( jobs -p | wc -l )
}

start_processing()
{
    myfile="$1"
    # DO SOMETHING HERE!!
    kill -SIGUSR1 $2
}

CPUS=$( lscpu | grep "^CPU(s):" | rev | cut -f 1 -d ' ' | rev )
POLLPERIOD=5  # 5s between each poll
np=0
trap subprocess_finished SIGUSR1

for myfile in *
do 
        start_processing "$myfile" $$ &
        np=$( jobs -p | wc -l )
        echo "$( date +'%Y-%m-%d %H:%M:%S' ) [$!] Starting #$np on $CPUS: $myfile"

        if [ $np -eq $CPUS ] 
        then
            # Wait for one CPU to be free
            trap subprocess_finished SIGUSR1
            while [ $np -eq $CPUS ]
            do
                sleep $POLLPERIOD
            done
        fi
    done
done

# wait for the last subprocesses
while [ ! -z "$( jobs -rp )" ]
do
    sleep $POLLPERIOD
done
person Fabien Bouleau    schedule 10.03.2014