Инициализация разделяемой памяти Posix

Мой вопрос заключается в инициализации памяти, полученной при использовании shm_open() и mmap() . Один общий совет, который я видел в нескольких местах, – это вызвать shm_open() с флагами O_CREAT|O_EXCL : если это удастся, мы будем первым пользователем общей памяти и можем его инициализировать, иначе мы не будем первыми, а разделяемая память уже был инициализирован другим процессом.

Однако из того, что я понимаю о shm_open и тестах, которые я делал в Linux, это не сработает: объекты общей памяти остаются в системе, даже после того, как последний пользователь объекта общей памяти отключен и закрыт. Простая тестовая программа, которая вызывает shm_open с O_CREAT|O_EXCL , затем закрывает дескриптор и завершает работу, будет успешной при первом запуске, но все равно будет терпеть неудачу во втором запуске, даже если никто не использует разделяемую память в это время.

Мне кажется, что (по крайней мере, в тестируемой системе) поведение shm_open в значительной степени идентично open() : если я модифицирую свою простую тестовую программу для записи чего-то в общую память (через указатель, полученный mmap ) и выйдите, то объект общей памяти будет постоянно сохранять его содержимое (я могу запустить еще одну простую программу для чтения ранее записанных данных).

Итак, совет об использовании shm_open с O_CREAT|O_EXCL просто неправильный, или я что-то O_CREAT|O_EXCL ?

Я знаю, что объект общей памяти можно удалить с помощью shm_unlink() , но, похоже, это вызовет больше проблем:

  1. Если процесс вызывается до вызова shm_unlink() мы возвращаемся к описанной выше проблеме.

  2. Если один процесс вызывает shm_unlink() а некоторые другие процессы все еще отображаются в одну и ту же разделяемую память, эти другие процессы будут продолжать использовать его, как обычно. Теперь, если приходит другой процесс и вызывает shm_open() с тем же именем и указанным O_CREAT , ему действительно удастся создать новый объект общей памяти с тем же именем, который полностью не связан с старым объектом общей памяти, который все еще используют другие процессы , Теперь у нас есть процесс, который пытается общаться с другими процессами через разделяемую память и совершенно не осознает, что использует неправильный канал.

Я использую семантику Windows, где объект общей памяти существует только до тех пор, пока по крайней мере один дескриптор открыт для него, поэтому этот материал Posix очень запутан.

Поскольку вы используете флаг O_EXCL я предполагаю, что у вас есть набор процессов, собранных вокруг одного мастера (создателя сегмента).

Затем ваш мастер-процесс создаст сегмент разделяемой памяти, используя вызов shm_open :

 shmid = shm_open("/insert/name/here", O_CREAT|O_EXCL, 0644); if (-1 == shmid) { printf("Oops ..\n"); } 

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

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

Как вы сказали, если процесс умирает до вызова процедуры shm_unlink , сегмент будет продолжать жить после этого. Чтобы этого избежать, в некоторых случаях вы можете определить свои собственные обработчики сигналов, чтобы выполнить операцию, когда принимаются сигналы, такие как SIGINT . Во всяком случае, вы не сможете покрыть беспорядок в случае отправки SIGKILL в ваш процесс.

EDIT: Чтобы быть более конкретным, использование O_CREAT | O_EXCL O_CREAT | O_EXCL ошибочен, когда это не нужно. В приведенном ниже примере вы можете видеть, что мастер должен создать сегмент, поэтому необходимы эти флаги. С другой стороны, ни один из подчиненных процессов не должен был бы его создавать. Таким образом, вы абсолютно запретите использование O_CREAT в связанных вызовах.

Теперь, если другой процесс вызывает shm_open(..., O_CREAT, ...) когда сегмент уже создан, он просто получит файловый дескриптор, относящийся к этому самому сегменту. Таким образом, он будет на правом канале (если у него есть права на это, см. Аргумент mode )

Вы можете сделать следующее: int test = shmget (key_t key, size, 0); Поместите это в звезду каждого процесса. Нулевой флаг здесь пытается открыть существующую разделяемую память, если ее еще не созданный тест равен -1, поэтому вы можете сделать проверку после этого утверждения, если тест -1 перейдет и создаст общую память, иначе вы просто получили идентификатор в существующей разделяемой памяти ….. Я надеюсь, что эта помощь