Общая память или mmap - Linux C/С++ IPC

Контекст - это Inter-Process-Communication, где один процесс ( "Сервер" ) должен отправлять структуры фиксированного размера во многие процессы прослушивания ( "Клиенты" ), запущенные на одном компьютере.

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

ОС - 64-битный RHEL.

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

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

Ответы

Ответ 1

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

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

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

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

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

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

Ответ 2

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

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

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

Ответ 3

Кроме того, что было предложено уже, я хотел бы предложить другой метод: IPv6 Node/Интерфейс Локальная многоадресная рассылка, т.е. многоадресная рассылка, связанная с интерфейсом loopback. 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/ приходят на ум.

Ответ 4

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

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