Мульти-ридерное решение IPC? - PullRequest
1 голос
/ 29 мая 2010

Я работаю над фреймворком на C ++ (пока что просто для удовольствия), который позволяет пользователю писать плагины, использующие стандартный API для потоковой передачи данных между собой. Там будет три основных транспортных механизма для данных: файлы, сокеты и какая-то система трубопроводов IPC. Система настроена таким образом, что для нефайловой передачи каждый поток может иметь несколько считывателей. То есть, как только сервер установит сокет, несколько компьютеров смогут подключаться и передавать данные. Я немного застрял в системе IPC с несколькими ридерами.

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

Мне любопытно, что люди предложили бы для решения для нескольких читателей что-то вроде этого? Достаточно ли низкие издержки на каналы или доменные сокеты, чтобы я мог просто открыть соединение с каждым читателем и выдать отдельные записи каждому читателю? Предполагается, что это будут значительные объемы данных (десятки мегапикселей / сек), поэтому производительность является обязательной.

Ответы [ 2 ]

0 голосов
/ 10 июня 2010

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

Для этого необходимо, чтобы сервер знал, сколько активных клиентов существует. Это, я полагаю, было бы возможно, если бы клиенты выполняли какую-либо регистрацию / вход в систему на сервере (почти наверняка это так, если они в процессе, но не обязательно так для клиентов вне процесса).

  • Предположим, есть N клиентов. Для этого примера предположим 100 активных клиентов.
  • Ведение двух счетных семафоров для каждой записи в циклической очереди. Если используются клиенты вне процесса, они должны быть разделены между процессами. Вызовите семафоры SemReady и SemDone.
  • Используйте SemReady, чтобы указать, что буфер готов для чтения клиентами. Сервер записывает в запись буфера, а затем устанавливает значение семафора в число клиентов (в данном случае 100). Подробнее об этом чуть позже.
  • Когда клиент хочет прочитать запись в очереди, он ожидает соответствующего семафора SemReady. Если начальное значение равно 100, то все 100 клиентов могут успешно получить семафор и «одновременно» прочитать данные.
  • Когда клиент завершает чтение / использование записи, он увеличивает / освобождает семафор SemDone.
  • Когда сервер хочет выполнить запись в буферную запись, ему необходимо убедиться в двух вещах: a) ни один из клиентов в настоящее время не читает его, и b) ни один из клиентов не начинает читать его, когда сервер пишет в него.
  • Поэтому, во-первых, заблокируйте любой дальнейший доступ к буферу, ожидая семафор SemReady, пока счетчик не станет равным нулю (очевидно, используйте нулевое время ожидания). Когда он достигает нуля, сервер знает, что никакие дополнительные клиенты не начнут его читать.
  • Чтобы знать, что клиенты работают с буфером, сервер использует семафор SemDone. Он проверяет SemDone и ждет, пока его значение не станет равным N минус количество ожиданий, которое он совершил в SemReady. Другими словами, если SemReady был в нуле, то это означает, что все клиенты читают запись буфера, поэтому SemDone должен быть в N (100), когда они сделаны. Если, однако, сервер ожидал 10 раз в SemReady, то SemDone должен быть в 90 (N-10), когда все клиенты сделаны.
  • Вышеуказанный шаг требует некоторого времени ожидания и проверки состояния «живучести» клиента в случае сбоя / выхода из клиента после получения SemReady и до выпуска SemDone. Кроме того, необходимо учитывать возможность регистрации нового клиента на этом этапе, чтобы синхронизировать значения счетчика семафоров.
  • Как только сервер обнаружил, что клиенты больше не читают буфер, он может сбросить SemDone на ноль, записать новые данные в запись и установить SemReady в N (100).
  • Сполосните и повторите.

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

Примечание 2 SemDone, вероятно, может быть целочисленным счетчиком, обработанным с атомными приращениями ... Я думаю, что в любом случае это возможно. Нужно немного подумать.

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

0 голосов
/ 30 мая 2010

Я разрабатываю медиа-сервер и обычно использую один ридер для группы всех активных сокетов одного класса. Вы можете использовать функцию select () (в блокирующем или неблокирующем режиме) для каждой группы для чтения сокетов, которые стали готовы к чтению. Когда данные сокета готовы или возникает новое соединение, я просто вызываю функцию обратного вызова notify для управления им.

Каждый ридер (который контролирует группу сокетов) может управляться отдельным потоком, избегая блокирования основных потоков при ожидании новых соединений или данных сокета.

...