Почему API IPC общей памяти POSIX C требует разрешений (чтение/запись) в нескольких местах?

Я пытаюсь использовать API IPC общей памяти POSIX в C, который в основном соответствует шаблону shm_open() -> ftruncate() -> mmap() -> use -> munmap() -> shm_unlink() (последние два являются необязательными IIUC). Я заметил, что первые два вызова требуют разных флагов, определяющих разрешения и параметры, но я не могу понять разницу. Со страниц руководства:

int shm_open(const char *name, int oflag, mode_t mode);
                               ---------  -----------
                                 FIRST      SECOND

oflag is a bit mask created by ORing together exactly one of O_RDONLY or O_RDWR
       O_RDONLY
              Open the object for read access.  A shared memory object opened in this way can be mmap(2)ed only for read (PROT_READ) access.

       O_RDWR Open the object for read-write access.

       O_CREAT
              Create  the shared memory object if it does not exist.  The user and group ownership of the object are taken from the corresponding effec‐
              tive IDs of the calling process, and the object's permission bits are set according to the low-order 9 bits of  mode,  except  that  those
              bits set in the process file mode creation mask (see umask(2)) are cleared for the new object.  A set of macro constants which can be used
              to define mode is listed in open(2).  (Symbolic definitions of these constants can be obtained by including <sys/stat.h>.)

а потом

void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
                                      --------
                                       THIRD

 The prot argument describes the desired memory protection of the mapping (and must not conflict with the open mode of the file).   It  is  either
       PROT_NONE or the bitwise OR of one or more of the following flags:

       PROT_EXEC  Pages may be executed.

       PROT_READ  Pages may be read.

       PROT_WRITE Pages may be written.

       PROT_NONE  Pages may not be accessed.

У меня вопрос: в чем разница между разрешениями, указанными в трех выделенных местах?

Несколько вещей, которые я заметил:

  • вы можете указать O_RDONLY в shm_open(), но тогда для выделения некоторого места в виртуальном файле с помощью ftruncate() вам все равно нужно иметь разрешения на запись (O_RDWR), так какой в ​​этом смысл?
  • зачем это нужно, и в том же вызове мне нужно установить битовую маску для прав доступа к файлам (SECOND в примере)
  • второй вопрос: тогда вы также указываете защиту доступа для mmap, но там также сказано, что After the mmap() call has returned, the file descriptor, fd, can be closed immediately without invalidating the mapping., поэтому я думаю, если вы хотите создать файл только для чтения (потому что другие процессы могут писать в него), вам нужно сначала открыть его с правами на запись , затем отобразить его в режиме только для чтения, а затем освободить fd?
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
64
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Запись файловой системы имеет права доступа, по крайней мере, три «группы» «авторизации» (чтение, запись, выполнение) для владельца, владельца группы и других. Они используются, когда процесс запрашивает доступ к этому объекту; запрошенные доступы проверяются на соответствие этим разрешениям.

Когда вы «открываете» что-то, вы запрашиваете намерение, это указывается в параметре oflag (я хочу прочитать RD_ONLY, или я хочу написать, или...). Иногда просишь открыть то, чего нет, и в то же время при необходимости создать это (O_CREAT). В этом случае вам необходимо указать, какие будут права доступа, через параметр mode.

Открыв этот объект, вы можете использовать его так, как вы заявили в начале. В случае объекта общей памяти вам необходимо сопоставить его с адресным пространством процесса, чтобы иметь возможность доступа к нему. В этом и состоит цель mmap — отобразить этот объект в каком-то месте памяти вашего процесса. Любой фрагмент памяти адресного пространства также защищен; например, код вашего процесса (по умолчанию) защищен от записи и т. д. Таким образом, если вы сопоставляете объект общей памяти, вам необходимо указать, какова будет защита этой памяти, в этом и заключается цель параметра prot.

Спасибо, первое отличие, которое я упустил, заключалось в том, что флаги mode учитываются только при создании нового файла с O_CREAToflag. Что касается второго: это тот же механизм, с помощью которого исполняемый файл, который я создаю, разрешен как user=rwx в файловой системе, но раздел text того же файла в памяти защищен от записи? Но тогда зачем нам указывать намерение для shm_openoflag, если общую память можно использовать только через mmap (которая тоже защищена)?

Alessandro Bertulli 28.05.2024 12:51

@AlessandroBertulli shm_open не знает, какую prot ценность вы намерены передать mmap. Он не может просто открыть чтение и запись по умолчанию, потому что текущие разрешения могут этого не допускать.

Ian Abbott 28.05.2024 13:06

@AlessandroBertulli Вы можете получить дескриптор файла, который для вас открывает какая-то библиотека (представьте, в режиме открытия чтения/записи), но ваше намерение состоит в том, чтобы просто прочитать внутри сопоставления и убедиться в этом ==> PROT_READ. У вас также могут быть разные намерения в разное время для одного и того же объекта.

Jean-Baptiste Yunès 29.05.2024 09:52

У меня нет ответа, кроме четкого ответа Жана-Батиста Юнеса и комментария Яна Эббота, однако есть некоторые аспекты, которые могут быть интересны и заслуживают изучения.

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

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

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

Одна из возможностей — вместо использования shm использовать ZeroMQ. Это может быть слишком радикальным переосмыслением архитектуры приложения, которое вы преследуете, но у него есть некоторые интересные свойства, которые могут представлять интерес.

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

Во-вторых, если единицы выполнения (потоки, процессы) обмениваются данными с помощью ZeroMQ, все остальные транспорты (ipc, tcp, vmci и т. д.) могут быть задействованы и смешаны/сопоставлены. Таким образом, исполнительные блоки можно легко преобразовать в потоки, отдельные процессы или процессы на отдельных машинах без каких-либо реальных изменений исходного кода. Его встроенная библиотека шифрования/безопасности (я думаю, libsodium) означает, что, если рассматривать ее как отдельные процессы, в приложении может быть обеспечена конфиденциальность/безопасность без необходимости подвергать все воздействию любого процесса, запускаемого пользователем.

Стоит обратить внимание на скорость inproc. Он копирует данные (сколько раз я не уверен, но, возможно, только один раз), тогда как использование общей памяти (через shm) не копирует данные. Стоит иметь в виду, что доступ к общей памяти из нескольких ядер (или, что еще хуже, из отдельных ЦП) сопряжен с некоторыми накладными расходами в микроэлектронике ЦП; между ядрами может происходить много разговоров по управлению кешем, и в зависимости от того, что именно делает приложение, может быть много промахов кеша. В общем, на современных процессорах, если это соответствует приложению, может иметь смысл иметь отдельную копию данных для разных потоков/процессов, опосредованную структурой модели актера, такой как ZeroMQ.

Другие вопросы по теме