LINUX.ORG.RU

Зачем «общая память» связана с файловой системой?

 


0

1

Читал страницы https://man7.org/linux/man-pages/man7/shm_overview.7.html и https://man7.org/linux/man-pages/man3/shm_open.3.html потом немного stackoverflow.

«On Linux, it searches for a tmpfs mount which is usually mounted under /dev/shm or /var/run/shm. they are by default created inside of /dev/shm, you can simply use test, and that will create /dev/shm/test and if you want them somewhere else, you have to mount a tmpfs file-system in that desired directory.»

Вопрос - зачем Linux создаёт файл в файловой системе? Общая память как раз нужна, чтобы без файлов обойтись… Понятно, что дискам обращений нет. Не нравится то, что это имя видно снаружи, что его могут занять или переназначить на нём права, чтобы файл не открывался. Или могут открыть приложением, которому туда доступа быть не должно…

★★★

Последнее исправление: Shushundr (всего исправлений: 1)

Ответ на: комментарий от Shushundr

Линукс его никак не реализует. shm_open() это функция glibc, внутри которой спрятан обычный open() к файлам в /dev/shm.

Нативный для линукса это shmget().

Но вот эти твои опасения:

Не нравится то, что это имя видно снаружи, что его могут занять или переназначить на нём права, чтобы файл не открывался. Или могут открыть приложением, которому туда доступа быть не должно…

совершенно левые. Доступ в юниксах ограничивается не спрятыванием имён, а настройкой owner/group/mode. Переназначить права файлу, у которого ты не owner, нельзя. Если там стоит mode 0700 то и прочитать его чужой uid тоже не сможет.

имя видно снаружи, что его могут занять

Чтобы не занимали имя файла, его можно создавать в /run/user/{uid}/ (через open()) - это точно такое же tmpfs как и /dev/shm.

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 2)
Ответ на: комментарий от firkax

Чтобы не занимали имя файла, его можно создавать в /run/user/{uid}/

Я не понял, как это поможет. https://renenyffenegger.ch/notes/Linux/fhs/run/user/uid/index

«The directory is created when the user (with $uid) logs in and is destroyed when he logs out again.»

Другие процессы от этого же пользователя вполне смогут занять.

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr

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

firkax ★★★★★
()
Ответ на: комментарий от annulen

Файлов то нет, но «украсть» его могут точно так же, если права (uid/mode) подходят. Вместо команды ls -al /dev/shm будет команда ipcs. Полностью анонимных блоков разделяемой памяти в линуксе не бывает.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Линукс его никак не реализует. shm_open() это функция glibc, внутри которой спрятан обычный open() к файлам в /dev/shm.

Почему ты думаешь, что именно внутри glibc, а не внутри ядра. Если ядро Linux стремится к POSIX-совместимости само, то было бы логично что все функции POSIX оно экспортирует.

Есть же в нём какая-то sys_shmget.

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 4)
Ответ на: комментарий от Shushundr

Я не думаю, а знаю, и не надо задавать глупые вопросы. Посмотри сам как shm_open() устроено если не веришь.

Если ядро Linux стремится к POSIX-совместимости

Откуда ты взял это заявление? Никуда оно не стремится. Более того, в POSIX нет спецификаций ядра, там только спецификации видимых юзерспейсному программисту функций.

Есть же в нём какая-то sys_shmget.

Я выше и писал что в линуксе есть нативный shmget(). А shm_open() - обёртка над обычным open().

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от Shushundr

Если ядро Linux стремится к POSIX-совместимости само, то было бы логично что все функции POSIX оно экспортирует.

Ядро Linux не реализует POSIX. Например, API pthreads полностью реализован в юзерспейсе.

annulen ★★★★★
()
Ответ на: комментарий от firkax

Можно ли выделить общую память таким образом, чтобы у неё были одинаковые адреса в адресных пространствах разных процессов?

Если я правильно понял, то адрес надо указать два раза, по одному в каждом процессе в момент вызова

void *shmat(int shmid, const void *shmaddr, int shmflg);

SHM_REMAP (Linux-specific) This flag specifies that the mapping of the segment should replace any existing mapping in the range starting at shmaddr and continuing for the size of the segment. (Normally, an EINVAL error would result if a mapping already exists in this address range.) In this case, shmaddr must not be NULL.

Но как вычислить, что надо передавать в параметр с именем shmaddr непонятно.

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 4)
Ответ на: комментарий от slovazap

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

Так может и не нужно, чтобы доступ получали независимые процессы? Может быть нужно оставить только зависимым процессам? Например передать хендл во время fork-а только дочерним процессам…

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr

Нет гарантий, что один и тот же участок адресов будет доступен в разных адресных пространствах.

В целом что где будет занято и свободно это implementation defined от версии ядра, glibc и некоторых крутилок в системе. А с включенной рандомизацией адресов ещё и от рандома зависит.

Также есть проблема гонки потоков - технически, почти любая функция glibc может вызывать под капотом аллокацию памяти. А любая аллокация памяти это потенциальный mmap, при этом алгоритм выбора свободного адреса - implementation defined. Таким образом, даже если ты нашёл каким-то образом (например, через /proc) свободный участок, то если у тебя >1 потока в программе (а потоки могут создавать в том числе динамические библиотеки без твоего ведома), то нет гарантий, что между поиском и вызовом mmap кто-то другой не сделает mmap раньше тебя и не займёт этот адрес.

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

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)

Мне вообще в принципе непонятно, как с этой общей памятью работать.

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

А затем они дружно используют всякие interlocked-алгоритмы, для того, чтобы не пользоваться семафорами (которые требуют вызовов в ядро и медленные).

Так?

Или как-то ещё? Если сделать память доступной на запись кому попало, её же сразу обнулят, чисто из вредности…

Shushundr ★★★
() автор топика
Ответ на: комментарий от KivApple

Нет гарантий, что один и тот же участок адресов будет доступен в разных адресных пространствах.

Но можно ведь поискать такой, который будет доступен. Маловероятно, что два процесса используют либо всю свою память, либо по половине, но строго непересекающейся… Виртуальная память огромна!

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr

Если сделать память доступной на запись кому попало, её же сразу обнулят, чисто из вредности

Из вредности можно открыть /proc/{pid}/mem и обнулить не только общую память. А в реальности подразумевается, что программы написаны тобой или твоими друзьями и общаются по некоему устоявшемуся протоколу.

Семафор может блокировать большую структуру данных, тогда главный выигрыш будет в отсутствии копирования, а не в отсутствии системных вызовов.

Также существуют lock-free структуры данных, которые позволяют обходиться без семафоров.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от Shushundr

SHM_REMAP делать не надо, ты же не хочешь, закрыв глаза, выгрузить то что было по этому адресу раньше? Очевидно, оно кем-то использовалось.

Способ другой: просто ищешь адреса, которые и там и там свободные, и мапишь на них. Искать можно как перебором (вызывать shmat() с разными shmaddr пока он не сможет его сделать) так и какими-то алгоритмами поумнее.

firkax ★★★★★
()
Ответ на: комментарий от KivApple

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

Про протоколу - это верно, это обязательно. Но друзья такие, что никому верить нельзя. Значит надо думать, как запускать под разными аккаунтами, если по-другому никак…

Shushundr ★★★
() автор топика
Ответ на: комментарий от Shushundr

Если тебе для такого нужны примеры то забей. Выбирай рандомные адреса например. Я почти уверен, что тебе shm вообще не нужно ни в каком виде, и ты просто страдаешь фигнёй.

firkax ★★★★★
()
Ответ на: комментарий от firkax

Можно просто посмотреть /proc/self/maps. Но для портабельности нужно обработать гонку, когда ты нашёл свободную область, но между находкой и вызовом mmap её кто-то занял и mmap обломался.

KivApple ★★★★★
()
Ответ на: комментарий от Shushundr

Просто распарси /proc/self/maps, там простой формат, можно хоть fscanf использовать. По факту ты можешь взять любой адрес, которого там нет. Можешь заодно и в процессы друзей заглянуть, если знаешь их pid и они под тем же юзером.

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

Можешь заодно и в процессы друзей заглянуть, если знаешь их pid и они под тем же юзером.

Они сами в свои карты могут заглянуть. А потом сообщить. Не обязательно же создавать в один заход. Сначала обменяться данными как попало (например через общую память с разными адресами), а потом уже создать общую память с одинаковыми адресами.

Мне только неясно, после присоединения этот кусок адресов появится в карте занятых кусков? Если да, это хорошо, если нет - плохо…

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 2)
Ответ на: комментарий от slovazap

Пока непонятно. Я не видел длинных разумных текстов на эту тему.

Механизмы (API’s) разделяемой памяти в *NIX-системах

System V

  • Классический механизм управления разделяемой памятью. Поддерживается большинством *NIX систем
  • Память делится между несвязными процессами.

POSIX shared memory

  • Память делится между несвязными процессами без нагрузки лишней нагрузки на файловую систему и ввод/вывод.
  • Полагается полее простым и эффективным, чем старые API

Shared mapping - mmap(2)

  • Shared anonymous mapping. Память делится между связными процессами. (Через fork() )
  • Shared file mapping. Память делится между несвязными процессами.
Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 2)
Ответ на: комментарий от Shushundr

Если да, это хорошо, если нет - плохо…

Появится, в этом псевдофайле все блоки выделенные mmap.

Они сами в свои карты могут заглянуть. А потом сообщить.

Можно и так. Но если выбрать одного главного, который за всех всё сделает, то это уменьшило бы оверхед.

Но вообще зачем собственно маппить по одинаковым адресам?

KivApple ★★★★★
()
Последнее исправление: KivApple (всего исправлений: 1)
Ответ на: комментарий от KivApple

вообще зачем собственно маппить по одинаковым адресам?

Чтобы работал рантайм языка программирования. Иначе надо всё будет делать через маршаллинг и весь эффект затеи исчезнет.

Shushundr ★★★
() автор топика

Интересно, можно ли создать процесс с uid такого пользователя, которого нет в системе? (хочу запустить несколько процессов и все с разными uid)

«Unprivileged processes may only set the effective user ID to the real user ID, the effective user ID or the saved set-user-ID.»

Непонятно, чем эти ID друг от друга отличаются

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

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 4)
Ответ на: комментарий от Shushundr

Пока непонятно. Я не видел длинных разумных текстов на эту тему.

А какого рода ты ожидаешь разумный текст про то чтобы сознательно ограничить функциональность?

slovazap ★★★★★
()
Ответ на: комментарий от Shushundr

Судя по тому, что пользовательская программа никого под другим пользователем запустить не может, это не самая популярная техника. Может быть неправы те, кто говорят, что любой процесс может перезаписать память любого другого процесса под тем же пользователем? Может для этого надо его под отладкой запускать, или права рута иметь?

Shushundr ★★★
() автор топика
Ответ на: комментарий от slovazap

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

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 1)
Ответ на: комментарий от KivApple

Из вредности можно открыть /proc/{pid}/mem и обнулить не только общую память.

Нельзя, потому что у /proc владелец root и прав на запись кому попало нет.

Хотяяя

$ echo "PID of this bash: $$"
PID of this bash: 1963389
$ ls -l /proc/ | grep 1963389 
dr-xr-xr-x  9 user             user                           0 фев 13 19:18 1963389
$ ls -l /proc/1963389 | grep mem
-rw-------  1 user user 0 фев 13 19:23 mem

Доступ на запись, конечно, выглядит странно.

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 3)

Зачем «общая память» связана с файловой системой?

Потому что файл это самый отказоустойчивый способ межпроцессного взаимодействия: если писатель помирает при записи данных, то файл не запишется полностью, а если читатель не вычитает данные полностью и затем не удалит файл, то файл останется на диске. Чтобы диск не изнашивался можно писать на раздел в ОЗУ.

Я бы бросил все эти «трубы», сокеты и общие памяти и просто писал и читал файлы. Просто, надежно и без выпендрежа. Да и всегда можно посмотреть будет кто и что когда пишет простым текстовым просмотрщиком.

Enthusiast ★★★
()
Ответ на: комментарий от Enthusiast

ну включи в freeorion автосейв между ходами (кажется включен по-умолчанию) и поиграй в него. Он тормозит и при сохранении и при считывании, даже если сохранять в tmpfs.

Shushundr ★★★
() автор топика
Последнее исправление: Shushundr (всего исправлений: 1)
Ответ на: комментарий от vbr

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

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

Shushundr ★★★
() автор топика
Ответ на: комментарий от slovazap

Не решай за других. Автору хочется чтобы никто третий поучаствовать в этом шаринге штатно не мог, имеет право, и вообще это вполне логичное желание.

firkax ★★★★★
()
Ответ на: комментарий от Shushundr

/proc/pid/mem можно открыть только затрассировав этот процесс. Об этом написано в proc(5) и ptrace(2). ptrace может быть ограничен Yama. Вобщем память процесса так просто не доступна.

iliyap ★★★★★
()
Ответ на: комментарий от Shushundr

Да, кажется всё так и есть. Если ты уже юзер (не рут) то управлять дальнейшим разделением прав сложновато.

Ещё посмотри man user_namespaces - я сам не изучал (там как-то всё сложно), но, возможно, оно чем-то поможет.

firkax ★★★★★
()
Ответ на: комментарий от Shushundr

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

firkax ★★★★★
()
Ответ на: комментарий от annulen

Ядро Linux не реализует POSIX. Например, API pthreads полностью реализован в юзерспейсе.

Что ты вкладываешь в «полностью»? Треды не существуют на уровне ядра, вместо этого в процессе по alarm и/или кооперативно переключаются ucontext_t-ы?

utf8nowhere ★★★
()