Разделение памяти между процессами с помощью mmap()

У меня линукс 2.6. У меня есть среда, в которой 2 процесса имитируют (используя общую память) обмен данными через простую реализацию режима передачи сообщений.

У меня есть клиентский процесс (разветвленный от родителя, который является сервером), который записывает структуру (сообщение) в область отображения памяти, созданную (после разветвления) с помощью:

message *m = mmap(NULL, sizeof(message), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0)

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

Проблема в том, что *m создается после fork(), и когда серверный процесс пытается получить доступ к указанной ячейке памяти, я получаю ошибку сегментации. Можно ли присоединить эту область памяти к разветвлению POST сервера после того, как клиент его создаст?

ПРИМЕЧАНИЕ. Я не хочу сопоставлять указатель на сообщение перед разветвлением (а затем делиться им с сервером), потому что обычно я не знаю, сколько сообщений клиент хочет отправить на сервер, а также может быть больше чем 1 клиентский процесс, поэтому я хотел бы создать новый блок общей памяти только тогда, когда клиенту нужно отправить сообщение, и отключить его после того, как сервер получил это сообщение.

ПРИМЕЧАНИЕ. Это для академических целей: я знаю, что это не лучший способ решить эту проблему, но мне просто нужно следовать этому пути.

Заранее спасибо!


person Andrea Sprega    schedule 14.02.2011    source источник


Ответы (3)


Можно ли присоединить эту область памяти к разветвлению POST сервера после того, как клиент его создаст?

MAP_ANONYMOUS|MAP_SHARED отображаемая память может быть доступна только процессу, который выполняет этот mmap() вызов, или его дочерним процессам. Другой процесс не может отобразить ту же память, потому что на эту память нельзя ссылаться из другого места, поскольку она анонимна.

Используя вызов shm_open(), можно создать именованную разделяемую память, на которую могут ссылаться и отображать несвязанные процессы.

person Maxim Egorushkin    schedule 14.02.2011
comment
Спасибо! Я использовал shm_open(), а затем mmap() с заданным fd, это частично решило проблему. Можно ли переименовать (используя rename()) строку тега, которую я передаю в shm_open() после mmapped fd? Я пытался сделать это, но затем, когда я пытаюсь использовать shm_open() из другого процесса, используя переименованный тег, я получаю ошибку шины во время выполнения. ПРИМЕЧАНИЕ. Я заметил, что в /sys/shm/ существует переименованный файл, поэтому переименование прошло успешно. - person Andrea Sprega; 14.02.2011
comment
shm_open("name") в Linux обычно переводится как open("/dev/shm/name"). Вы должны иметь возможность переименовать его, как обычный файл. Обратите внимание, что /dev/shm/ — это точка монтирования tmpfs по умолчанию в Linux. Он может быть нанесен на карту в другом месте. См. google.com/codesearch/p?hl=en#xy1xtVWIKOQ/pub/glibc/releases/ - person Maxim Egorushkin; 14.02.2011
comment
Спасибо за замечание. Есть ли у вас какие-либо идеи, почему переименование может вызвать ошибку шины, если я попытаюсь получить доступ к зоне общей памяти? - person Andrea Sprega; 14.02.2011
comment
shm_open() приводит к SIGBUS? - person Maxim Egorushkin; 14.02.2011
comment
да. Клиентский процесс выполняет функции shm_open(), mmap() и затем переименовывает() файл. Затем сервер снова использует shm_open() (с переименованным тегом), и я получаю в терминале ошибку шины, которая, как мне кажется, соответствует SIGBUS. - person Andrea Sprega; 14.02.2011
comment
Попробуйте запустить под отладчиком и посмотрите, что вызывает SIGBUS, я не думаю, что это shm_open(). SIGBUS часто является результатом доступа к отображенной памяти за пределами конца файла (файл был усечен во время отображения). - person Maxim Egorushkin; 14.02.2011
comment
/dev/shm или shm_open по умолчанию имеют ограничение квоты в половину размера физической памяти. Вы можете изначально использовать shm_open и mmap для большего пространства памяти, но когда вы пишете сверх ограничения или кто-то другой также использует /dev/shm или shm_open, так что оставшееся пространство равно нулю, ваша программа напрямую получает SIGBUS. - person jclin; 17.03.2016

Только для тех, кто читает этот вопрос в 2018 году и позже. Теперь решение состоит в том, чтобы использовать memfd_create для создания анонимного файла и использовать сокет unix для передачи этого дескриптора файла другому процессу.

memfd_create — системный вызов только для Linux.

person Lothar    schedule 12.02.2018

Это не сработает.

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

Вы не можете предполагать совместное использование указателей таким образом.

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

Два связанных процесса, которые вызывают mmap() после разветвления, могут вернуть один и тот же указатель, указывающий на другую память. На самом деле это крайне вероятно.

person MarkR    schedule 14.02.2011