Общая память или mmap - Linux C / C ++ IPC - PullRequest
15 голосов
/ 29 января 2011

Контекст - межпроцессное взаимодействие, когда один процесс («Сервер») должен отправить структуры фиксированного размера многим прослушивающим процессам («Клиентам»), работающим на той же машине.

Я оченьудобно делать это в Socket Programming.Чтобы ускорить обмен данными между Сервером и Клиентами и уменьшить количество копий, я хочу попробовать использовать общую память (shm) или mmaps.

Операционная система RHEL 64bit.

Поскольку я новичок, пожалуйста, предложите, какой мне следует использовать.Я был бы признателен, если бы кто-нибудь указал мне книгу или онлайн-ресурс, чтобы узнать то же самое.

Спасибо за ответы.Я хотел добавить, что сервер (сервер рыночных данных) обычно получает многоадресные данные, что приводит к тому, что он «отправляет» около 200 000 структур в секунду «клиентам», где каждая структура составляет примерно 100 байт.Превосходит ли реализация shm_open / mmap сокеты только для больших блоков данных или большого объема небольших структур?

Ответы [ 4 ]

22 голосов
/ 29 января 2011

Я бы использовал mmap вместе с shm_open для сопоставления разделяемой памяти с виртуальным адресным пространством процессов.Это относительно прямолинейно и чисто:

  • вы идентифицируете свой сегмент совместно используемой памяти с каким-то символическим именем, что-то вроде "/myRegion"
  • с shm_open, вы открываете дескриптор файлав этом регионе
  • с помощью ftruncate вы увеличиваете сегмент до нужного вам размера
  • с помощью mmap вы отображаете его в свое адресное пространство

* 1019Интерфейсы * и Co имеют (по крайней мере исторически) тот недостаток, что они могут иметь ограничение в максимальном объеме памяти, который вы можете отобразить.

Затем все инструменты синхронизации потоков POSIX (pthread_mutex_t, * 1023)*, sem_t, pthread_rwlock_t, ...) имеют интерфейсы инициализации, которые позволяют использовать их также в общем контексте процесса.Все современные дистрибутивы Linux поддерживают это.

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

Как подсказывает @R, если у вас несколько считывателей, pthread_rwlock_t, вероятно,Лучшая структура замка для использования.

7 голосов
/ 30 января 2011

Помимо того, что уже было предложено, я хотел бы предложить другой метод: IPv6 узел / интерфейс Локальная многоадресная передача, то есть многоадресная передача, ограниченная интерфейсом обратной связи.http://www.iana.org/assignments/ipv6-multicast-addresses/ipv6-multicast-addresses.xml#ipv6-multicast-addresses-1

Поначалу это может показаться довольно тяжелым, но большинство ОС используют петлевые сокеты в архитектуре с нулевым копированием.Страницам, сопоставленным с параметром buf, переданным в send, будет назначено дополнительное сопоставление, и они помечены как копии при записи, так что если отправляющая программа перезаписывает данные в них или освобождает содержимое, они будут сохранены.

Вместо передачи необработанных структур вы должны использовать надежную структуру данных.На ум приходят Netstrings http://cr.yp.to/proto/netstrings.txt и BSON http://bsonspec.org/.

7 голосов
/ 29 января 2011

Однажды я реализовал библиотеку IPC с использованием сегментов общей памяти; это позволило мне избежать копирования (вместо того, чтобы копировать данные из памяти отправителя, в пространство ядра, а затем из пространства ядра в память получателя, я мог напрямую копировать данные из отправителя в память получателя).

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

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

2 голосов
/ 29 января 2011

Выбор между интерфейсом POSIX shm_open/mmap и более старой версией System V shmop не будет иметь большого значения, потому что после системных вызовов инициализации вы получите ту же ситуацию: область памяти, которая используется совместноразличные процессы.Если ваша система поддерживает его, я бы рекомендовал использовать shm_open/mmap, поскольку этот интерфейс лучше спроектирован.

Затем вы используете область разделяемой памяти в качестве общей доски, где все процессы могут выполнять свои записи.Сложная часть заключается в синхронизации процессов, обращающихся к этой области.Здесь я рекомендую избегать придумывания собственной схемы синхронизации, которая может быть чрезвычайно сложной и подверженной ошибкам.Вместо этого используйте существующую работающую реализацию на основе сокетов для синхронизации доступа между процессами и используйте общую память только для передачи больших объемов данных между процессами.Даже с этой схемой вам потребуется центральный процесс для координации распределения буферов, поэтому эта схема того стоит, только если у вас есть очень большие объемы данных для передачи.Или используйте библиотеку синхронизации, например Boost.Interprocess .

.
...