У меня есть следующие требования для механизма IPC в Linux:
Существует один процесс производителя, но несколько процессов потребителя. Процессы-потребители не являются потомками процесса-производителя. Они воспитываются самостоятельно.
Транспортируемые сообщения имеют структуру POD фиксированного размера.
Нам нужно использовать фиксированный объем памяти для этого механизма. Механизм, подобный кольцевому буферу, кажется здесь идеальным.
Производителю нужно бежать очень быстро и никогда не ждать покупателей. Вместо этого ему необходимо перезаписать записи в буфере фиксированного размера (для IPC), и потребители должны обнаружить эту проблему и просто догнать производителя, пропустив промежуточные сообщения в случае перебора.
Потребители могут приходить и уходить в любой момент, и не должно быть явного рукопожатия между единственным производителем и временными потребителями, когда они приходят и уходят. Поэтому, когда приходят потребители, они просто начинают читать последние доступные сообщения и падают в любое время, когда производители не знают об этом.
У меня есть следующее решение прямо сейчас:
Я создаю кольцевой буфер записей фиксированного размера, который записывается одним процессом и читается многими. Кольцевой буфер создается поверх файла, отображенного в памяти, в / tmp.
Код кольцевого буфера таков, что производители никогда не блокируют ожидание того, чтобы кто-либо из потребителей использовал записи. Вместо этого потребители обнаруживают, когда кольцевой буфер обернулся в середине чтения / обработки записи, и просто догоняют последние. Я делаю это с парой последовательностей продюсеров. Я могу остановиться на этом, если это необходимо, но, кажется, не очень актуально. Таким образом, производители скачут на полной скорости, а потребители, которые отстают, обнаруживают эту среднюю ошибку чтения и переходят к последней записи.
На данный момент это работает нормально. Теперь я пытаюсь выяснить, как добавить некоторую сигнализацию в микс, чтобы потребителям не приходилось чередовать чтение последовательностей производителей в ожидании нового сообщения. У меня есть некоторые дополнительные требования к сигнальной части:
Сигнализация должна быть своего рода механизмом вещания. Это следует из требования одного производителя / multiple_consumers. Таким образом, несколько процессов должны быть в состоянии проснуться, когда производители сигнализируют о них. Учитывая, что производитель не знает ни о каком из потребителей, кажется, что нам нужен какой-то именованный ресурс для этой сигнализации.
Механизм сигнализации должен быть совместим с другими обычными сигналами, которые можно ожидать при использовании select / epoll_wait. Потребители, которые читают этот механизм IPC / ring_buffer, ожидают записи в другие несвязанные каналы / сокеты и т. Д., И они используют выбор этих FD. Было бы идеально иметь возможность просто создать FD из этого механизма, который потребители могли бы просто добавить к своему вызову выбора. Точно так же, если потребитель ожидает нескольких таких ring_buffers, мы должны иметь возможность заблокировать все из них и проснуться, как только любой из них подаст сигнал.
Учитывая эти требования, я исключил несколько вариантов:
Условные переменные: мы не можем заблокировать несколько из них от потребителя. Они не selectable
или.
Именованные каналы: Нам нужен именованный канал для каждого потребителя, и это подразумевает какое-то рукопожатие производителя / потребителя, которого мы хотим избежать.
eventfd: eventfds не названы, так что кажется, что они являются только решением, если передача сигналов происходит между родительским и дочерним процессами. В моем сценарии есть процессы, которые запускаются независимо.
Сокет домена Unix: кажется, что здесь нет никаких средств вещания, поэтому я не уверен, будет ли это работать без явного сокета для каждого потребителя. Это идет вразрез с требованием отсутствия рукопожатия.
Я немного растерялся и не вижу другого хорошего варианта. Мне кажется, что UDP многоадресная передача, вероятно, будет работать. Все потребители могут быть частью многоадресной группы (создайте сокет с SO_REUSEADDR
) и единственный производитель может отправлять сообщения в этой группе, чтобы сигнализировать потребителям. Но это кажется очень тяжелым и сложным. Есть ли другие хорошие механизмы, чтобы это произошло? Я готов работать напрямую с Futex API
если это помогает до тех пор, пока он объединяется с блокировкой других несвязанных FD с использованием select / epoll.
Я рекомендую отправить сигнал через DBus. Зачем накатывать свой собственный IPC, если вы можете использовать зрелую платформу, которая уже делает то, что вы хотите?
Эта страница должна помочь вам начать:
https://dbus.freedesktop.org/doc/dbus-tutorial.html
В конце он включает ссылки на API Qt и GLib. Я не использовал ни того, ни другого, а написал свою собственную оболочку на основе Boost.ASIO для низкоуровневого API, хотя это довольно сложное мероприятие.
https://dbus.freedesktop.org/doc/api/html/
Кстати, для отправки блоков двоичных данных вы захотите добавить аргумент типа DBUS_TYPE_ARRAY
из DBUS_TYPE_BYTE
, к вашему сообщению. Обратите внимание, что DBUS_TYPE_STRING
не может содержать нулевые байты или недопустимые последовательности Юникода.
Обновить: Еще одна библиотека, которая недавно привлекла мое внимание, называется sd-bus. Вот хороший обзор & руководство:
Других решений пока нет …