Разделяемая память

Эта статья находится на начальном уровне проработки, в одной из её версий выборочно используется текст из источника, распространяемого под свободной лицензией
Материал из энциклопедии Руниверсалис
Наглядное представление разделяемой памяти

Разделяемая память (англ. Shared memory) является самым быстрым средством обмена данными между процессами[1].

В других средствах межпроцессового взаимодействия (IPC) обмен информацией между процессами проходит через ядро, что приводит к переключению контекста между процессом и ядром, т.е. к потерям производительности[2].

Техника разделяемой памяти позволяет осуществлять обмен информацией через общий для процессов сегмент памяти без использования системных вызовов ядра. Сегмент разделяемой памяти подключается в свободную часть виртуального адресного пространства процесса[3]. Таким образом, два разных процесса могут иметь разные адреса одной и той же ячейки, подключенной разделяемой памяти.

Краткое описание работы

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

Реализация технологии «клиент—сервер»

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

Сценарий использования разделяемой памяти

  1. Сервер получает доступ к разделяемой памяти, используя семафор.
  2. Сервер производит запись данных в разделяемую память.
  3. После завершения записи данных сервер освобождает доступ к разделяемой памяти с помощью семафора.
  4. Клиент получает доступ к разделяемой памяти, запирая доступ к этой памяти для других процессов с помощью семафора.
  5. Клиент производит чтение данных из разделяемой памяти, а затем освобождает доступ к памяти с помощью семафора.

Программная реализация

В программном обеспечении разделяемой памятью называют:

  • Метод межпроцессного взаимодействия (IPC), то есть способ обмена данными между программами, работающими одновременно. Один процесс создаёт область в оперативной памяти, которая может быть доступна для других процессов.
  • Метод экономии памяти, путём прямого обращения к тем исходным данным, которые при обычном подходе являются отдельными копиями исходных данных, вместо отображения виртуальной памяти или описанного метода . Такой подход обычно используется для разделяемых библиотек и для XIP.

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

Обмен данными через разделяемую память используется, например, для передачи изображений между приложением и X-сервером на Unix-системах или внутри объекта IStream возвращаемого CoMarshalInterThreadInterfaceInStream в библиотеке COM под Windows.

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

В UNIX-подобных операционных системах

POSIX предоставляет стандартизированное API для работы с разделяемой памятью — POSIX Shared Memory. Одной из ключевых особенностей операционных систем семейства UNIX является механизм копирования процессов (системный вызов fork()), который позволяет создавать анонимные участки разделяемой памяти перед копированием процесса и наследовать их процессами-потомками. После копирования процесса разделяемая память будет доступна как родительскому, так и дочернему процессу.[3][4]

Существует два разных подхода к подключению и использованию разделяемой памяти:

Разделяемая память в стиле UNIX System V

UNIX System V предоставляет набор функций языка C, позволяющий работать с разделяемой памятью[7]:

  • shmget — создание сегмента разделяемой памяти с привязкой к целочисленному идентификатору, либо анонимного сегмента разделяемой памяти (при указании вместо идентификатора значения IPC_PRIVATE)[8];
  • shmctl — установка параметров сегмента памяти[9];
  • shmat — подключение сегмента к адресному пространству процесса[4];
  • shmdt — отключение сегмента от адресного пространства процесса[10].

Именованная разделяемая память подразумевает ассоциацию с каждым участком памяти уникального числового ключа в рамках операционной системы, по которому в дальнейшем можно подключить разделяемую память в другом процессе.[8]

Разделяемая память POSIX

POSIX позволяет связать с объектом разделяемой памяти файловый дескриптор, что является более унифицированным механизмом, чем механизм UNIX System V. Для работы с памятью могут быть использованы следующие функции языка C:

  • shm_open — создание или подключение объекта разделяемой памяти POSIX по его имени[6];
  • shm_unlink — удаление объекта разделяемой памяти по его имени (при этом сегмент разделяемой памяти будет существовать, пока не будет отключен от всех процессов)[11];
  • ftruncate — задаёт или изменяет размер разделяемой памяти (или отображённого в память файла)[12];
  • mmap — подключает существующий или создаёт анонимный сегмент разделяемой памяти к адресному пространству процесса[3].

В операционных системах семейства Windows

В операционной системе Windows для создания разделяемой памяти используются функции CreateFileMapping и MapViewOfFile[13] из MSDN.

Поддержка в языках программирования

Некоторые библиотеки языка C++ предлагают доступ к работе с разделяемой памятью в кроссплатформенном виде. Например, библиотека Boost предоставляет класс boost::interprocess::shared_memory_object[14] для POSIX-совместимых операционных систем, а библиотека Qt предоставляет класс QSharedMemory, унифицирующий доступ к разделяемой памяти для разных операционных систем с некоторыми ограничениями[15].

В Java 7 под операционной системой GNU/Linux разделяемая память может быть реализована отображением файла из каталога /dev/shm/ (либо /run/shm/, в зависимости от дистрибутива) в память[16] с помощью метода map класса java.nio.MappedByteBuffer[17].

Поддержка разделяемой памяти осуществлена во многих других языках программирования. Так, PHP предоставляет API[18] для создания разделяемой памяти, чьи функции схожи с функциями POSIX.

См. также

Примечания

  1. Колисниченко Денис Николаевич. Разработка Linux-приложений. — БХВ-Петербург, 2012-01-01. — 430 с. — ISBN 9785977507479. Архивная копия от 23 июля 2016 на Wayback Machine
  2. Hyok-Sung Choi, Hee-Chul Yun. Context Switching and IPC Performance Comparison between uClinux and Linux on the ARM9 based Processor (англ.) // Samsung Electronics : Технический отчёт. — 2004. Архивировано 6 марта 2016 года.
  3. 3,0 3,1 3,2 mmap. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 6 декабря 2015 года.
  4. 4,0 4,1 shmat. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 30 декабря 2015 года.
  5. System Interfaces Chapter 2. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 8 января 2016 года.
  6. 6,0 6,1 shm_open. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 21 ноября 2015 года.
  7. Kay A. Robbins. UNIX systems programming: communication, concurrency, and threads. — Prentice Hall PTR, 2003. — С. 512. Архивная копия от 22 сентября 2014 на Wayback Machine
  8. 8,0 8,1 shmget. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 5 марта 2016 года.
  9. shmctl. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 7 декабря 2015 года.
  10. shmdt. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 12 декабря 2015 года.
  11. shm_unlink. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 9 ноября 2015 года.
  12. ftruncate. pubs.opengroup.org. Дата обращения: 3 января 2016. Архивировано 1 февраля 2016 года.
  13. Creating Named Shared Memory. Дата обращения: 26 июня 2014. Архивировано 5 июня 2014 года.
  14. Sharing memory between processes - 1.60.0. www.boost.org. Дата обращения: 4 января 2016. Архивировано 29 декабря 2015 года.
  15. QSharedMemory Class | Qt Core 5.5. doc.qt.io. Дата обращения: 4 января 2016. Архивировано 7 декабря 2015 года.
  16. shm_overview(7) - Linux manual page. man7.org. Дата обращения: 4 января 2016. Архивировано 4 января 2016 года.
  17. MappedByteBuffer (Java Platform SE 7 ). docs.oracle.com. Дата обращения: 4 января 2016. Архивировано 15 января 2016 года.
  18. Shared Memory Functions in PHP-API. Дата обращения: 26 июня 2014. Архивировано 25 июня 2014 года.