Карта сайта Kansoftware
НОВОСТИУСЛУГИРЕШЕНИЯКОНТАКТЫ
Разработка программного обеспечения

Sharing memory between processes

Boost , The Boost C++ Libraries BoostBook Documentation Subset , Chapter 16. Boost.Interprocess

Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

PrevUpHomeNext

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

Рассмотрим, что происходит, когда серверный процесс хочет отправить HTML-файл клиентскому процессу, который находится в одной машине с использованием сетевых механизмов:

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

Как мы видим, существует две копии: одна из памяти в сеть, а другая из сети в память. И эти копии сделаны с использованием вызовов операционной системы, которые обычно стоят дорого. Общая память позволяет избежать этих накладных расходов, но нам необходимо синхронизировать оба процесса:

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

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

Чтобы использовать общую память, мы должны выполнить 2 основных шага:

  • Запрос на операционную систему сегмента памяти, который может быть разделен между процессами. Пользователь может создать/уничтожить/открыть эту память, используяобъект общей памяти:Объект, представляющий память, который может быть отображен одновременно в адресное пространство более чем одного процесса..
  • Связать часть этой памяти или всю память с адресным пространством процесса вызова. Операционная система ищет достаточно большой диапазон адресов памяти в адресном пространстве процесса вызова и отмечает этот диапазон адресов как особый диапазон. Изменения в этом диапазоне адресов автоматически видны другим процессом, который также нанес на карту тот же объект общей памяти.

После успешного завершения двух этапов процесс может начать запись и чтение из адресного пространства для отправки и получения данных из других процессов. Теперь давайте посмотрим, как мы можем сделать это с помощьюBoost.Interprocess:

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

#include <boost/interprocess/shared_memory_object.hpp>

Как мы уже упоминали, мы должны использовать класс<shared_memory_object>для создания, открытия и уничтожения сегментов общей памяти, которые могут быть отображены несколькими процессами. Мы можем указать режим доступа этого объекта общей памяти (только для чтения или записи), как если бы это был файл:

  • Создайте сегмент общей памяти. Броски, если они уже созданы:
using boost::interprocess;
shared_memory_object shm_obj
   (create_only                  //only create
   ,"shared_memory"              //name
   ,read_write                   //read-write mode
   );
  • Для открытия или создания сегмента общей памяти:
using boost::interprocess;
shared_memory_object shm_obj
   (open_or_create               //open or create
   ,"shared_memory"              //name
   ,read_only                    //read-only mode
   );
  • Открыть только общий сегмент памяти. Броски, если их нет:
using boost::interprocess;
shared_memory_object shm_obj
   (open_only                    //only open
   ,"shared_memory"              //name
   ,read_write                   //read-write mode
   );

Когда создается объект общей памяти, его размер равен 0. Чтобы установить размер общей памяти, пользователь должен использовать вызов функции<truncate>в общей памяти, которая была открыта с атрибутами чтения-записи:

shm_obj.truncate(10000);

Поскольку общая память имеет сохранение ядра или файловой системы, пользователь должен явно уничтожить ее. Операция<remove>может не вернуть ложную информацию, если общая память не существует, файл открыт или файл все еще отображается другими процессами:

using boost::interprocess;
shared_memory_object::remove("shared_memory");

Подробнее об этом<shared_memory_object>см. в<boost::interprocess::shared_memory_object>.

После создания или открытия процесс просто должен отображать объект общей памяти в адресном пространстве процесса. Пользователь может отображать всю общую память или только ее часть. Процесс картирования осуществляется с использованием класса<mapped_region>. Класс представляет собой область памяти, которая была отображена из общей памяти или с других устройств, которые также имеют возможности отображения (например, файлы).<mapped_region>может быть создано из любого<memory_mappable>объекта, и, как вы можете себе представить,<shared_memory_object>является<memory_mappable>объектом:

using boost::interprocess;
std::size_t ShmSize = ...
//Map the second half of the memory
mapped_region region
   ( shm                      //Memory-mappable object
   , read_write               //Access mode
   , ShmSize/2                //Offset from the beginning of shm
   , ShmSize-ShmSize/2        //Length of the region
   );
//Get the address of the region
region.get_address();
//Get the size of the region
region.get_size();

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

Более подробную информацию о<mapped_region>см. в<boost::interprocess::mapped_region>классе ссылки.

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

#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
int main(int argc, char *argv[])
{
   using namespace boost::interprocess;
   if(argc == 1){  //Parent process
      //Remove shared memory on construction and destruction
      struct shm_remove
      {
         shm_remove() { shared_memory_object::remove("MySharedMemory"); }
         ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
      } remover;
      //Create a shared memory object.
      shared_memory_object shm (create_only, "MySharedMemory", read_write);
      //Set size
      shm.truncate(1000);
      //Map the whole shared memory in this process
      mapped_region region(shm, read_write);
      //Write all the memory to 1
      std::memset(region.get_address(), 1, region.get_size());
      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
   }
   else{
      //Open already created shared memory object.
      shared_memory_object shm (open_only, "MySharedMemory", read_only);
      //Map the whole shared memory in this process
      mapped_region region(shm, read_only);
      //Check that memory was initialized to 1
      char *mem = static_cast<char*>(region.get_address());
      for(std::size_t i = 0; i < region.get_size(); ++i)
         if(*mem++ != 1)
            return 1;   //Error checking memory
   }
   return 0;
}

Boost.Interprocessобеспечивает переносимую общую память с точки зрения семантики POSIX. Некоторые операционные системы не поддерживают общую память, как определено в POSIX:

  • Операционные системы Windows обеспечивают общую память, используя память, поддерживаемую файлом подкачки, но семантика времени жизни отличается от тех, которые определены POSIX (см. разделНативные окна совместно используемой памятидля получения дополнительной информации).
  • Некоторые системы UNIX не полностью поддерживают объекты общей памяти POSIX.

В этих платформах общая память эмулируется с отображенными файлами, созданными в папке «boost_interprocess», созданной во временном каталоге файлов. В платформах Windows, если в реестре присутствует ключ «Common AppData», в этом каталоге создается папка «boost_interprocess» (в XP обычно «C:\Documents and Settings\All Users\Application Data» и в Vista «C:\ProgramData»). Для платформ Windows без этого ключа реестра и систем Unix общая память создается в системном каталоге временных файлов («/tmp» или аналогичном).

Из-за этой эмуляции общая память имеет срок службы файловой системы в некоторых из этих систем.

<shared_memory_object>обеспечивает статическую<remove>функцию для удаления общих объектов памяти.

Эта функцияможет выйти из строя, если общие объекты памяти не существуют или она открыта другим процессом. Обратите внимание, что эта функция аналогична стандартной функции C<int remove(constchar*path)>. В системах UNIX<shared_memory_object::remove>вызывает<shm_unlink>:

  • Функция удалит имя объекта общей памяти, названного строкой, указанной по имени.
  • Если одна или несколько ссылок на объект общей памяти существуют, когда он не связан, имя будет удалено до возвращения функции, но удаление содержимого объекта памяти будет отложено до тех пор, пока все открытые и картографические ссылки на объект общей памяти не будут удалены.
  • Даже если объект продолжает существовать после последнего вызова функции, повторное использование имени впоследствии приведет к тому, что создание экземпляра<boost::interprocess::shared_memory_object>будет вести себя так, как будто не существует объекта общей памяти этого имени (то есть попытка открыть объект с этим именем потерпит неудачу, и объект с тем же именем может быть создан снова).

В операционных системах Windows текущая версия поддерживает обычно приемлемую эмуляцию несвязанного поведения UNIX: файл переименовывается со случайным именем и помечается какдля удаления при закрытии последней открытой ручки.

Создание сегмента общей памяти и отображение его может быть немного утомительным, когда задействовано несколько процессов. Когда процессы связаны посредством вызова операционной системы<fork()>в системах UNIX, более простой способ доступен с использованием анонимной совместно используемой памяти.

Эта функция была реализована в системах UNIX, отображающих устройство<\dev\zero>или просто использующих<MAP_ANONYMOUS>в системном вызове, соответствующем POSIX<mmap>.

Эта функция завернута вBoost.Interprocessс использованием функции<anonymous_shared_memory()>, которая возвращает объект<mapped_region>, содержащий анонимный сегмент совместно используемой памяти, который может совместно использоваться связанными процессами.

Вот пример:

#include <boost/interprocess/anonymous_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <cstring>
int main ()
{
   using namespace boost::interprocess;
   try{
      //Create an anonymous shared memory segment with size 1000
      mapped_region region(anonymous_shared_memory(1000));
      //Write all the memory to 1
      std::memset(region.get_address(), 1, region.get_size());
      //The segment is unmapped when "region" goes out of scope
   }
   catch(interprocess_exception &ex){
      std::cout << ex.what() << std::endl;
      return 1;
   }
   return 0;
}

После создания сегмента может быть использован вызов<fork()>, так что<region>используется для передачи двух связанных процессов.

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

По этой причине не существует эффективного способа имитации стойкости ядра или файловой системы с использованием общей памяти нативных окон.Boost.Interprocessэмулирует общую память с использованием карт памяти файлов. Это обеспечивает переносимость между операционными системами POSIX и Windows.

Однако доступ к общей памяти нативных окон является общим запросом пользователейBoost.Interprocess, потому что они хотят получить доступ к общей памяти, созданной с помощью другого процесса, который не используетBoost.Interprocess. Для управления нативными окнами общая памятьBoost.Interprocessпредлагает класс<windows_shared_memory>.

Создание совместно используемой памяти Windows немного отличается от создания совместно используемой памяти: размер сегмента должен быть указан при создании объекта и не может быть указан через<truncate>, как с объектом совместно используемой памяти. Обратите внимание, что когда последний процесс, связанный с общей памятью, разрушается, общая память разрушается, так чтонет постоянствас нативными окнами общей памяти.

Обмен памятью между сервисами и пользовательскими приложениями также отличается. Для совместного использования памяти между службами и пользовательскими приложениями имя совместно используемой памяти должно начинаться с глобального префикса пространства имен<"Global\\">. Это глобальное пространство имен позволяет процессам на нескольких клиентских сессиях общаться с приложением службы. Серверный компонент может создавать общую память в глобальном пространстве имен. Затем клиентская сессия может использовать префикс «Global», чтобы открыть эту память.

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

Давайте повторим тот же пример, представленный для портативного объекта общей памяти: Серверный процесс создает объект общей памяти, отображает его и инициализирует все байты до значения. После этого клиентский процесс открывает общую память, отображает ее и проверяет правильность инициализации данных. Позаботьтесь о том, чтобы, если сервер выходит до того, как клиент подключится к общей памяти, соединение с клиентом выйдет из строя, поскольку сегмент совместно используемой памяти разрушается, когда к памяти не подключены никакие процессы.

Это серверный процесс:

#include <boost/interprocess/windows_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
int main(int argc, char *argv[])
{
   using namespace boost::interprocess;
   if(argc == 1){  //Parent process
      //Create a native windows shared memory object.
      windows_shared_memory shm (create_only, "MySharedMemory", read_write, 1000);
      //Map the whole shared memory in this process
      mapped_region region(shm, read_write);
      //Write all the memory to 1
      std::memset(region.get_address(), 1, region.get_size());
      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
      //windows_shared_memory is destroyed when the last attached process dies...
   }
   else{
      //Open already created shared memory object.
      windows_shared_memory shm (open_only, "MySharedMemory", read_only);
      //Map the whole shared memory in this process
      mapped_region region(shm, read_only);
      //Check that memory was initialized to 1
      char *mem = static_cast<char*>(region.get_address());
      for(std::size_t i = 0; i < region.get_size(); ++i)
         if(*mem++ != 1)
            return 1;   //Error checking memory
      return 0;
   }
   return 0;
}

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

Во многих системах UNIX ОС предлагает другой механизм совместно используемой памяти, сегменты совместно используемой памяти XSI (X/Open System Interfaces), также известные как «System V». Этот механизм общей памяти довольно популярен и портативный, и он не основан на семантике картографирования файлов, но использует специальные функции<shmget>,<shmat>,<shmdt>,<shmctl>....

В отличие от сегментов совместно используемой памяти POSIX, сегменты совместно используемой памяти XSI не идентифицируются по именам, а по «ключам», обычно создаваемым с помощью<ftok>. Сегменты общей памяти XSI имеют срок службы ядра и должны быть явно удалены. XSI не поддерживает копирование и частичное картирование общей памяти, но поддерживает анонимную общую память.

Boost.Interprocessпредлагает простые<xsi_shared_memory>и управляемые<managed_xsi_shared_memory>классы совместно используемой памяти для облегчения использования совместно используемой памяти XSI. Он также включает в себя создание ключей с простым классом<xsi_key>.

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

Это серверный процесс:

#include <boost/interprocess/xsi_shared_memory.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <cstdlib>
#include <string>
using namespace boost::interprocess;
void remove_old_shared_memory(const xsi_key &key)
{
   try{
      xsi_shared_memory xsi(open_only, key);
      xsi_shared_memory::remove(xsi.get_shmid());
   }
   catch(interprocess_exception &e){
      if(e.get_error_code() != not_found_error)
         throw;
   }
}
int main(int argc, char *argv[])
{
   if(argc == 1){  //Parent process
      //Build XSI key (ftok based)
      xsi_key key(argv[0], 1);
      remove_old_shared_memory(key);
      //Create a shared memory object.
      xsi_shared_memory shm (create_only, key, 1000);
      //Remove shared memory on destruction
      struct shm_remove
      {
         int shmid_;
         shm_remove(int shmid) : shmid_(shmid){}
         ~shm_remove(){ xsi_shared_memory::remove(shmid_); }
      } remover(shm.get_shmid());
      //Map the whole shared memory in this process
      mapped_region region(shm, read_write);
      //Write all the memory to 1
      std::memset(region.get_address(), 1, region.get_size());
      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
   }
   else{
      //Build XSI key (ftok based)
      xsi_key key(argv[0], 1);
      //Create a shared memory object.
      xsi_shared_memory shm (open_only, key);
      //Map the whole shared memory in this process
      mapped_region region(shm, read_only);
      //Check that memory was initialized to 1
      char *mem = static_cast<char*>(region.get_address());
      for(std::size_t i = 0; i < region.get_size(); ++i)
         if(*mem++ != 1)
            return 1;   //Error checking memory
   }
   return 0;
}

Картирование файлов - это ассоциация содержимого файла с частью адресного пространства процесса. Система создает картографирование файлов, чтобы связать файл и адресное пространство процесса. Картированная область - это часть адресного пространства, которую процесс использует для доступа к содержимому файла. Одно отображение файла может иметь несколько отображенных областей, так что пользователь может связать части файла с адресным пространством процесса без отображения всего файла в адресном пространстве, поскольку файл может быть больше, чем все адресное пространство процесса (файл изображения DVD 9 ГБ в обычных 32-битных системах). Процессы считывают и записывают в файл с помощью указателей, как и в случае с динамической памятью. Картирование файлов имеет следующие преимущества:

  • Единообразное использование ресурсов. Файлы и память можно обрабатывать с помощью одних и тех же функций.
  • Автоматическая синхронизация файловых данных и кэш из ОС.
  • Повторное использование утилит C++ (контейнеров STL, алгоритмов) в файлах.
  • Общая память между двумя или более приложениями.
  • Позволяет эффективно работать с большими файлами без отображения всего файла в память
  • Если несколько процессов используют одно и то же отображение файлов для создания отображенных областей файла, представления каждого процесса содержат идентичные копии файла на диске.

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

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

Чтобы использовать файлы с картой памяти, мы должны выполнить 2 основных шага:

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

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

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

#include <boost/interprocess/file_mapping.hpp>

Во-первых, мы должны связать содержимое файла с адресным пространством процесса. Для этого мы должны создать отображаемый объект, который представляет этот файл. Это достигается вBoost.Interprocessсоздании<file_mapping>объекта:

using boost::interprocess;
file_mapping m_file
   ("/usr/home/file"       //filename
   ,read_write             //read-write mode
   );

Теперь мы можем использовать вновь созданный объект для создания картографированных регионов. Более подробную информацию об этом классе см. в ссылке<boost::interprocess::file_mapping>.

После создания картографирования файлов процесс просто должен отображать общую память в адресном пространстве процесса. Пользователь может отображать всю общую память или только ее часть. Процесс картирования осуществляется с использованием класса<mapped_region>. Как мы уже говорили ранее Класс представляет собой область памяти, которая была отображена из общей памяти или с других устройств, которые также имеют возможности отображения:

using boost::interprocess;
std::size_t FileSize = ...
//Map the second half of the file
mapped_region region
   ( m_file                   //Memory-mappable object
   , read_write               //Access mode
   , FileSize/2               //Offset from the beginning of shm
   , FileSize-FileSize/2      //Length of the region
   );
//Get the address of the region
region.get_address();
//Get the size of the region
region.get_size();

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

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

//Flush the whole region
region.flush();
//Flush from an offset until the end of the region
region.flush(offset);
//Flush a memory range starting on an offset
region.flush(offset, size);

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

Более подробную информацию о<mapped_region>см. в<boost::interprocess::mapped_region>классе ссылки.

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

#include <boost/interprocess/file_mapping.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <vector>
#include <cstring>
#include <cstddef>
#include <cstdlib>
int main(int argc, char *argv[])
{
   using namespace boost::interprocess;
   //Define file names
   const char *FileName  = "file.bin";
   const std::size_t FileSize = 10000;
   if(argc == 1){ //Parent process executes this
      {  //Create a file
         file_mapping::remove(FileName);
         std::filebuf fbuf;
         fbuf.open(FileName, std::ios_base::in | std::ios_base::out
                              | std::ios_base::trunc | std::ios_base::binary);
         //Set the size
         fbuf.pubseekoff(FileSize-1, std::ios_base::beg);
         fbuf.sputc(0);
      }
      //Remove on exit
      struct file_remove
      {
         file_remove(const char *FileName)
            : FileName_(FileName) {}
         ~file_remove(){ file_mapping::remove(FileName_); }
         const char *FileName_;
      } remover(FileName);
      //Create a file mapping
      file_mapping m_file(FileName, read_write);
      //Map the whole file with read-write permissions in this process
      mapped_region region(m_file, read_write);
      //Get the address of the mapped region
      void * addr       = region.get_address();
      std::size_t size  = region.get_size();
      //Write all the memory to 1
      std::memset(addr, 1, size);
      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
   }
   else{  //Child process executes this
      {  //Open the file mapping and map it as read-only
         file_mapping m_file(FileName, read_only);
         mapped_region region(m_file, read_only);
         //Get the address of the mapped region
         void * addr       = region.get_address();
         std::size_t size  = region.get_size();
         //Check that memory was initialized to 1
         const char *mem = static_cast<char*>(addr);
         for(std::size_t i = 0; i < size; ++i)
            if(*mem++ != 1)
               return 1;   //Error checking memory
      }
      {  //Now test it reading the file
         std::filebuf fbuf;
         fbuf.open(FileName, std::ios_base::in | std::ios_base::binary);
         //Read it to memory
         std::vector<char> vect(FileSize, 0);
         fbuf.sgetn(&vect[0], std::streamsize(vect.size()));
         //Check that memory was initialized to 1
         const char *mem = static_cast<char*>(&vect[0]);
         for(std::size_t i = 0; i < FileSize; ++i)
            if(*mem++ != 1)
               return 1;   //Error checking memory
      }
   }
   return 0;
}

Как мы видели, и<shared_memory_object>, и<file_mapping>объекты могут быть использованы для создания<mapped_region>объектов. Картографированная область, созданная из общего объекта памяти или отображения файлов, относится к одному и тому же классу, и это имеет много преимуществ.

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

В примере, который мы видели, содержимое файла или общей памяти отображается в адресное пространство процесса, но адрес был выбран операционной системой.

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

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

Поэтому первый совет при отображении общей памяти и картированных файлов памяти — избегать использования необработанных указателей, если вы не знаете, что делаете. Используйте смещения между данными или относительными указателями, чтобы получить функциональность указателя, когда объект, помещенный в отображаемую область, хочет указать на объект, размещенный в той же отображаемой области.Boost.Interprocessпредлагает интеллектуальный указатель под названием<boost::interprocess::offset_ptr>, который можно безопасно разместить в общей памяти и который можно использовать для указания на другой объект, размещенный в том же файле с общей памятью / памятью.

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

Для отображения объекта в фиксированном адресе пользователь может указать этот адрес в конструкторе<mappedregion>:

mapped_region region ( shm                         //Map shared memory
                     , read_write                  //Map it as read-write
                     , 0                           //Map from offset 0
                     , 0                           //Map until the end
                     , (void*)0x3F000000           //Map it exactly there
                     );

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

Как уже упоминалось, пользователь не может сопоставить отображаемый объект памяти с любым адресом, и он может указать смещение отображаемого объекта, которое эквивалентно началу области отображения, с произвольным значением. Большинство операционных систем ограничивают адрес отображения и смещение отображаемого объекта множеством значения, называемогоразмером страницы. Это связано с тем, что операционная системавыполняет операции отображения по целым страницам.

Если используется фиксированный адрес отображения, параметрысмещенияиадресадолжны быть кратны этому значению. Это значение обычно составляет 4 КБ или 8 КБ для 32-битных операционных систем.

//These might fail because the offset is not a multiple of the page size
//and we are using fixed address mapping
mapped_region region1( shm                   //Map shared memory
                     , read_write            //Map it as read-write
                     , 1                     //Map from offset 1
                     , 1                     //Map 1 byte
                     , (void*)0x3F000000     //Aligned mapping address
                     );
//These might fail because the address is not a multiple of the page size
mapped_region region2( shm                   //Map shared memory
                     , read_write            //Map it as read-write
                     , 0                     //Map from offset 0
                     , 1                     //Map 1 byte
                     , (void*)0x3F000001     //Not aligned mapping address
                     );

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

//Map one byte of the shared memory object.
//A whole memory page will be used for this.
mapped_region region ( shm                    //Map shared memory
                     , read_write             //Map it as read-write
                     , 0                      //Map from offset 0
                     , 1                      //Map 1 byte
                     );

Операционная система зарезервирует целую страницу, которая не будет повторно использоваться никаким другим отображением, поэтому мы собираемся потратить впустую.(размер страницы — 1)байт. Если мы хотим эффективно использовать ресурсы операционной системы, мы должны создать области, размер которых кратенразмеру страницыбайтам. Если пользователь указывает следующие две отображаемые области для файла с байтами<2*page_size>:

//Map the first quarter of the file
//This will use a whole page
mapped_region region1( shm                //Map shared memory
                     , read_write         //Map it as read-write
                     , 0                  //Map from offset 0
                     , page_size/2        //Map page_size/2 bytes
                     );
//Map the rest of the file
//This will use a 2 pages
mapped_region region2( shm                //Map shared memory
                     , read_write         //Map it as read-write
                     , page_size/2        //Map from offset 0
                     , 3*page_size/2      //Map the rest of the shared memory
                     );

В этом примере половина страницы теряется в первом отображении, а другая половина теряется во втором, потому что смещение не кратно размеру страницы. Картографирование с минимальным использованием ресурсов будет составлять карту целых страниц:

//Map the whole first half: uses 1 page
mapped_region region1( shm                //Map shared memory
                     , read_write         //Map it as read-write
                     , 0                  //Map from offset 0
                     , page_size          //Map a full page_size
                     );
//Map the second half: uses 1 page
mapped_region region2( shm                //Map shared memory
                     , read_write         //Map it as read-write
                     , page_size          //Map from offset 0
                     , page_size          //Map the rest
                     );

Как получить размер страницы? Класс<mapped_region>имеет статическую функцию, которая возвращает это значение:

//Obtain the page size of the system
std::size_t page_size = mapped_region::get_page_size();

Операционная система также может ограничивать количество отображенных областей памяти на процесс или на систему.

Когда два процесса создают отображенную область одного и того же отображаемого объекта, два процесса могут передавать запись и чтение этой памяти. Процесс может создать объект C++ в этой памяти, чтобы второй процесс мог использовать его. Тем не менее, отображаемая область, разделяемая несколькими процессами, не может содержать какой-либо объект C++, потому что не каждый класс готов быть объектом, разделяемым процессом, особенно если отображаемая область отображается в разных адресах в каждом процессе.

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

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

Ссылки страдают от той же проблемы, что и указатели (в основном потому, что они реализуются как указатели). Однако в настоящее время невозможно создать полностью работоспособную интеллектуальную ссылку на C++ (например,<operator .()>не может быть перегружен). Из-за этого, если пользователь хочет поместить объект в общую память, объект не может иметь какой-либо (умной или нет) ссылки в качестве члена.

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

Указатель виртуальной таблицы и виртуальная таблица находятся в адресном пространстве процесса, который конструирует объект, поэтому, если мы разместим класс с виртуальной функцией или виртуальным базовым классом, виртуальный указатель, размещенный в общей памяти, будет недействительным для других процессов, и они будут падать.

Эту проблему очень трудно решить, так как каждый процесс требует отдельного указателя виртуальной таблицы, и объект, содержащий этот указатель, разделяется во многих процессах. Даже если мы нанесем на карту регион в одном и том же адресе в каждом процессе, виртуальная таблица может быть в другом адресе в каждом процессе. Чтобы включить виртуальные функции для объектов, разделенных между процессами, необходимы глубокие изменения компилятора, и виртуальные функции пострадают от потери производительности. Вот почемуBoost.Interprocessне имеет никакого плана поддержки виртуальных функций и виртуального наследования в отображенных областях, разделенных между процессами.

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

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


PrevUpHomeNext

Статья Sharing memory between processes раздела The Boost C++ Libraries BoostBook Documentation Subset Chapter 16. Boost.Interprocess может быть полезна для разработчиков на c++ и boost.




Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.



:: Главная :: Chapter 16. Boost.Interprocess ::


реклама


©KANSoftWare (разработка программного обеспечения, создание программ, создание интерактивных сайтов), 2007
Top.Mail.Ru

Время компиляции файла: 2024-08-30 11:47:00
2025-07-04 23:15:24/0.017989873886108/0