Как использовать Unix-доменный сокет с bash

Вопрос или проблема

Прежде всего, я знаю о 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. Такие сокеты обеспечивают:

  1. Двунаправленную коммуникацию: В отличие от FIFO, которые ориентированы на однонаправленный поток данных, доменные сокеты позволяют одновременно считывать и записывать информацию.

  2. Высокая скорость и производительность: Поскольку они работают в рамках одного компьютера, они могут быстрее передавать данные по сравнению с сетевыми сокетами.

  3. Удобство использования: Файловый интерфейс позволяет удобно манипулировать сокетами через стандартные ПОСИКС-совместимые инструменты и команды.

Пример

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

Потоковая служба

  1. Создание сервера:

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

    socat STDIO UNIX-LISTEN:myuds,reuseaddr,fork

    Здесь STDIO связывает стандартный ввод/вывод с сокетом, UNIX-LISTEN:myuds указывает на создание сокета с прослушиванием, а fork обеспечивает возможность многопользовательского доступа.

  2. Создание клиента:

    Во втором и последующих терминалах выполните:

    socat UNIX-CLIENT:myuds STDIO

    Это создаст клиента, который соединяется с сервером через myuds сокет.

Служба на основе дейтаграмм

  1. Создание сервера:

    В первом терминале:

    socat STDIO UNIX-RECV:myuds,reuseaddr

    Здесь сервер настроен для получения данных.

  2. Создание клиентов:

    Во втором терминале и далее:

    socat UNIX-SENDTO:myuds STDIO

    Клиенты будут отправлять данные на указанный сокет.

Ответы на ваши вопросы

  1. Наследование файловых дескрипторов:

    Файловые дескрипторы в Bash могут быть унаследованы дочерними процессами только в том случае, если они были открыты перед созданием дочернего процесса. Использование /proc/$PID/fd/ – это способ получить доступ к файловым дескрипторам процесса по идентификатору процесса, что может быть необходимо, если файл дескриптор не был явно передан дочернему процессу. Управление дескрипторами через read dataA <&5 фактически делает то же самое, предполагая, что дескриптор 5 был установлен ранее корректно.

  2. Коммуникация между основным процессом и дочерним:

    В вашем случае каждая пара exec и дочерний процесс создает самостоятельные каналы связи. Если у вас есть сценарий, где главный процесс должен взаимодействовать напрямую с дочерним без дополнительных промежуточных процессов, вы должны обеспечить передачу соответствующих дескрипторов между этими процессами. Используйте socat и передачу стандартного ввода/вывода для организации прямой двунаправленной связи, как описано в примерах выше, или настройте главный процесс как сервер, который управляет соединениями клиентов.

Заключение

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

Оцените материал
Добавить комментарий

Капча загружается...