Вопрос или проблема
Прежде всего, я знаю о FIFO для IPC, но прочитал о unix domain sockets, которые являются двусторонними, и я хотел проверить, что возможно с командной строкой и bash.
Я спросил Chat GPT, но информация, которую я получил, была бесполезной, и примеры не работали.
Я хотел добиться того, чтобы два процесса могли одновременно писать друг другу или читать, без концепции FIFO.
Если я установлю дескриптор файла для чтения на FIFO, я также смогу писать с двух или более процессов в FIFO, но данные смешиваются, и первый читающий читает все смешанное содержимое.
Процесс A должен получать то, что пишет процесс B, а процесс B должен получать то, что пишет процесс A.
Я смог это сделать с помощью следующего тестового кода:
файл bash: processA
while true; do
for ((i=0;i<10;i++)); do
echo "$((i+1))A" > "/proc/$PID/fd/6";
sleep 1;
done
while read -t 0.01 dataB; do echo "Получено от процесса B: $dataB"; done
done
файл bash: processB
while true; do
for ((i=0;i<10;i++)); do
echo "$((i+1))B";
sleep 0.5;
done
while read -t 0.01 dataA <"/proc/$PID/fd/5"; do echo "Получено от процесса A: $dataA" > /dev/tty; done
done
терминал 1:
exec 6> >(nc -lU unixSocket | PID=$$ bash processA)
В процессе A:
После этого я могу писать в unix socket через “/proc/$PID/fd/6”,
читать из unix socket с помощью stdin
и stdout идет в терминал.
терминал 2:
exec 5< <(PID=$$ bash processB | nc -U unixSocket)
В процессе B:
После этого я могу писать в unix socket с помощью stdout,
читать из unix socket с помощью “/proc/$PID/fd/5”
писать в терминал с помощью /dev/ttx
а stdin находится на клавиатуре.
Первый вопрос: Почему файловые дескрипторы 5 и 6 не наследуются от подпроцессов, открытых в подстановке процесса?
Мне нужно получить их через экспорт PID – почему?
Второй вопрос: процессы A и B могут общаться двусторонне, но мне нужно открыть еще два главных процесса, которые выполняют команду exec для перенаправления файловых дескрипторов и запуска подстановки процессаexec 5< <(PID=$$ bash processB | nc -U unixSocket)
.
Но что я действительно хочу, так это такое взаимодействие между главным процессом и дочерним процессом, и не более того.
Сейчас у меня так: главный 1 открывает дочерний 1, главный 2 открывает дочерний 2, и дочерний 1 может общаться двусторонне с дочерним 2.
Спасибо за вашу помощь заранее
В ответ на ваш вопрос 1: откуда вы знаете, что файловые дескрипторы не наследуются? Вместо read dataA < /proc/$PID/fd/5
вы должны быть в состоянии сделать read dataA <&5
. Разве это не так?
Что касается вопроса 2, я предполагаю, вы спрашиваете, как это расширить для нескольких клиентов? Для этого необходимо либо поведение fork/exec для обработки каждого нового подключения клиента, если UDS находится в режиме потокового обслуживания, либо перевести UDS в режим обслуживания датаграмм.
Я никогда не использовал netcat для UDS, и за 10-секундный поиск я не могу найти документацию, которая объясняла бы, как его использовать. Каждый раз, когда я имел дело с UDS в shell-скрипте, я использовал инструмент командной строки под названием socat.
Если предположить, что вам нужен режим датаграмм, в первом терминале запустите сервер:
socat STDIO UNIX-RECV:myuds,reuseaddr
Во втором терминале запустите клиент:
socat UNIX-SENDTO:myuds STDIO
Повторяйте эту клиентскую команду столько раз, сколько вам нужно. Если вы хотите потоковое обслуживание, запустите сервер так:
socat STDIO UNIX-LISTEN:myuds,reuseaddr,fork
(Без опции fork
эта команда примет только одно соединение и завершится, когда соединение будет закрыто).
И запустите потоковые клиенты так:
socat UNIX-CLIENT:myuds STDIO
Страница man для socat подробно документирует все его адресные спецификации, которые он поддерживает. Он делает все, что делает netcat, и даже больше, с более расширяемым синтаксисом.
.
Ответ или решение
Использование Unix-доменных сокетов с Bash может показаться сложной задачей, особенно если вы уже знакомы с другими методами межпроцессного взаимодействия, такими как FIFO. Однако, Unix-доменные сокеты предоставляют более гибкий и двунаправленный канал связи, что делает их мощным инструментом для IPC. Давайте разберёмся, как настроить двунаправленную коммуникацию между двумя процессами в Bash, используя Unix-доменные сокеты, и ответим на ваши вопросы.
Теория
Unix-доменные сокеты позволяют программам обмениваться данными в пределах одной машины, используя файловую систему в качестве пространства имен. Они могут работать в потоковом режиме, как TCP-сокеты, или в дейтаграммовом режиме, как UDP. Такие сокеты обеспечивают:
-
Двунаправленную коммуникацию: В отличие от FIFO, которые ориентированы на однонаправленный поток данных, доменные сокеты позволяют одновременно считывать и записывать информацию.
-
Высокая скорость и производительность: Поскольку они работают в рамках одного компьютера, они могут быстрее передавать данные по сравнению с сетевыми сокетами.
-
Удобство использования: Файловый интерфейс позволяет удобно манипулировать сокетами через стандартные ПОСИКС-совместимые инструменты и команды.
Пример
Ваши эксперименты с nc (netcat) показали, что основную функциональность можно реализовать, но лучшим инструментом для работы с Unix-доменными сокетами в некоторых случаях может быть socat. Давайте разберем два сценария использования: потоковой службы и службы на основе дейтаграмм.
Потоковая служба
-
Создание сервера:
В первом терминале выполните:
socat STDIO UNIX-LISTEN:myuds,reuseaddr,fork
Здесь
STDIO
связывает стандартный ввод/вывод с сокетом,UNIX-LISTEN:myuds
указывает на создание сокета с прослушиванием, аfork
обеспечивает возможность многопользовательского доступа. -
Создание клиента:
Во втором и последующих терминалах выполните:
socat UNIX-CLIENT:myuds STDIO
Это создаст клиента, который соединяется с сервером через
myuds
сокет.
Служба на основе дейтаграмм
-
Создание сервера:
В первом терминале:
socat STDIO UNIX-RECV:myuds,reuseaddr
Здесь сервер настроен для получения данных.
-
Создание клиентов:
Во втором терминале и далее:
socat UNIX-SENDTO:myuds STDIO
Клиенты будут отправлять данные на указанный сокет.
Ответы на ваши вопросы
-
Наследование файловых дескрипторов:
Файловые дескрипторы в Bash могут быть унаследованы дочерними процессами только в том случае, если они были открыты перед созданием дочернего процесса. Использование
/proc/$PID/fd/
– это способ получить доступ к файловым дескрипторам процесса по идентификатору процесса, что может быть необходимо, если файл дескриптор не был явно передан дочернему процессу. Управление дескрипторами черезread dataA <&5
фактически делает то же самое, предполагая, что дескриптор 5 был установлен ранее корректно. -
Коммуникация между основным процессом и дочерним:
В вашем случае каждая пара
exec
и дочерний процесс создает самостоятельные каналы связи. Если у вас есть сценарий, где главный процесс должен взаимодействовать напрямую с дочерним без дополнительных промежуточных процессов, вы должны обеспечить передачу соответствующих дескрипторов между этими процессами. Используйтеsocat
и передачу стандартного ввода/вывода для организации прямой двунаправленной связи, как описано в примерах выше, или настройте главный процесс как сервер, который управляет соединениями клиентов.
Заключение
Использование Unix-доменных сокетов в Bash позволяет создавать более сложные и эффективные системы межпроцессного взаимодействия, чем те, что достижимы при помощи традиционных FIFO. Инструменты вроде socat существенно упрощают этот процесс, предлагая богатый набор функций для манипуляции сокетами. Понимание наследования файловых дескрипторов и моделей связи поможет настроить эффективную архитектуру вашего приложения, соответствующую требованиям производительности и удобства использования.