Есть ли что-то вроде shm_open () без имени файла? - PullRequest
2 голосов
/ 16 апреля 2019

Функция POSIX shm_open() возвращает дескриптор файла, который можно использовать для доступа к общей памяти. Это чрезвычайно удобно, потому что можно использовать все традиционные механизмы для управления дескрипторами файлов, чтобы также управлять общей памятью.

Единственным недостатком является то, что shm_open() всегда хочет имя файла. Поэтому мне нужно сделать это:

// Open with a clever temp file name and hope for the best.
fd = shm_open(tempfilename, O_RDWR | O_CREAT | O_EXCL, 0600);

// Immediately delete the temp file to keep the shm namespace clean.
shm_unlink(tempfilename);

// Then keep using fd -- the shm object remains as long as there are open fds.

Такое использование tempfilename трудно сделать переносимым и надежным. Интерпретация имени файла (что такое пространство имен, как обрабатываются разрешения) отличается в разных системах.

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

mmap() с MAP_ANON|MAP_SHARED замечательно, но вместо файлового дескриптора он дает указатель. Указатель не сохраняется за границей exec и не может быть отправлен другому процессу через сокет домена Unix, как дескрипторы файлов.

Файловый дескриптор, возвращаемый shm_open(), также не выдерживает границы exec по умолчанию: определение POSIX говорит, что установлен флаг дескриптора файла FD_CLOEXEC, связанный с новым файловым дескриптором. Но это возможно сбросить флаг, используя fcntl() на MacOS, Linux, FreeBSD, OpenBSD, NetBSD, DragonFlyBSD и, возможно, других операционных системах.

Ответы [ 4 ]

2 голосов
/ 16 апреля 2019

Библиотека для решения проблемы

Мне удалось написать библиотеку , которая обеспечивает простой интерфейс:

int shm_open_anon(void);

Библиотека компилируется без предупреждений и успешно запускает тестовую программу для Linux, Solaris, MacOS, FreeBSD, OpenBSD, NetBSD, DragonFlyBSD и Haiku. Возможно, вы сможете адаптировать его для других операционных систем; пожалуйста, отправьте запрос на удаление, если вы это сделаете.

Библиотека возвращает дескриптор файла с установленным флагом close-on-exec. Вы можете очистить этот флаг, используя fcntl() во всех поддерживаемых операционных системах, что позволит вам передать fd через exec(). Тестовая программа демонстрирует, что это работает.

Методы реализации, используемые в библиотеке

В файле readme библиотеки содержатся очень точные примечания о том, что было сделано и что не было сделано для каждой ОС. Вот краткое изложение основных вещей.

Есть несколько непереносимых вещей, которые более или менее эквивалентны shm_open() без имени файла:

  • FreeBSD может принимать SHM_ANON в качестве пути к shm_open() с 2008 года.

  • Linux имеет системный вызов memfd_create(), начиная с версии ядра 3.17.

  • В более ранних версиях Linux можно использовать mkostemp(name, O_CLOEXEC | O_TMPFILE), где name - это что-то вроде /dev/shm/XXXXXX. Обратите внимание, что здесь мы вообще не используем shm_open() - mkostemp() неявно использует совершенно обычный вызов open(). Linux монтирует специальную файловую систему с поддержкой памяти в /dev/shm, но некоторые дистрибутивы используют вместо нее /run/shm, поэтому здесь есть подводные камни. И вам все еще нужно shm_unlink () временный файл.

  • OpenBSD имеет вызов shm_mkstemp() с момента выпуска 5.4. Вам все еще нужно shm_unlink() временный файл, но по крайней мере его легко создать безопасно.

Для других ОС я сделал следующее:

  1. Определите зависящий от ОС формат для аргумента name в POSIX shm_open(). Обратите внимание, что нет имени, которое вы можете передать, которое является абсолютно переносимым. Например, NetBSD и DragonFlyBSD имеют противоречивые требования по поводу косых черт в имени. Это применимо, даже если ваша цель - использовать именованный объект shm (для которого был разработан API POSIX) вместо анонимного (как мы делаем здесь).

  2. Добавьте несколько случайных букв и цифр к имени (читая из /dev/random). Это в основном то, что делает mktemp(), за исключением того, что мы не проверяем, существует ли наше случайное имя в файловой системе. Интерпретация аргумента name сильно варьируется, поэтому нет разумного способа сопоставить его фактическому имени файла. Также Solaris не всегда обеспечивает mktemp(). Во всех практических целях введенная нами случайность обеспечит уникальное имя для доли секунды, которая нам нужна.

  3. Откройте объект shm с таким именем через shm_open(name, O_RDWR | O_CREAT | O_EXCL | O_NOFOLLOW, 0600). В астрономическом шансе, что наше случайное имя файла уже существует, O_EXCL в любом случае вызовет сбой этого вызова, так что никакого вреда не будет. Разрешения 0600 (владелец для чтения-записи) необходимы в некоторых системах вместо пустых разрешений 0.

  4. Немедленно позвоните shm_unlink(), чтобы избавиться от случайного имени. Описатель файла остается для нашего использования.

Эта техника не гарантируется для работы с POSIX, но:

  1. Аргумент shm_open() name недостаточно задан POSIX, поэтому ничто другое не может работать.
  2. Я позволю приведенному выше списку совместимости говорить за себя.

Наслаждайтесь.

2 голосов
/ 16 апреля 2019

Нет, нет. Поскольку как для модели с общей памятью System V, так и для сопоставления общих файлов POSIX для IPC требуются операции с файлом, для выполнения сопоставления всегда требуется файл.

mmap() с MAP_ANON|MAP_SHARED отлично, но вместо файла дескриптор дает указатель. Указатель не выживает над граница exec и не может быть отправлена ​​другому процессу через домен Unix сокет как файловые дескрипторы

Как Джон Боллинджер говорит,

Ни отображение памяти, созданное с помощью mmap(), ни общая память POSIX сегменты, полученные с помощью shm_open() ни сегментов разделяемой памяти System V полученные через shmat() сохраняются через exec.

В памяти должно быть известное место для встреч и обмена информацией. Вот почему файл является требованием. Таким образом, после exec ребенок может повторно подключиться к соответствующей общей памяти.

1 голос
/ 16 апреля 2019

Такое использование tempfilename трудно сделать переносимым и надежным.Интерпретация имени файла (что такое пространство имен, как обрабатываются разрешения) отличается в разных системах.

Вы можете mkstemp создать уникальное имя файла в /dev/shm/ или/tmp и откройте файл для вас.Затем вы можете unlink имя файла, чтобы никакой другой процесс не мог открыть этот файл, кроме процесса, который вернул дескриптор файла из mkstemp.

mkstemp(): СООТВЕТСТВИЕ СТАНДАРТАМ 4.3BSD, POSIX.1-2001.

0 голосов
/ 17 апреля 2019

Почему бы не создать его с правами доступа к 0? Таким образом, ни один процесс не сможет «открыть» его и позволить вам безопасно отсоединить его сразу после?

...