Как выбрать фиксированный адрес для отображения общей памяти - PullRequest
13 голосов
/ 09 мая 2011

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

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

segment = new boost::interprocess::managed_shared_memory(
    boost::interprocess::open_or_create,
    "MySegmentName",
    1048576, // alloc size
    (void *)0x400000000LL // fixed address
);

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

Ответы [ 4 ]

15 голосов
/ 09 мая 2011

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

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

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

Таким образом, вам нужно, чтобы программы достигли консенсуса по адресу памяти.Это означает, что несколько адресов могут быть опробованы и отклонены.Если возможно, что через некоторое время после запуска появится новая программа, поиск консенсуса придется начинать заново .Алгоритм будет выглядеть примерно так:

  1. Программа А предлагает адрес памяти X всем остальным программам.
  2. Другие программы отвечают с истинным или ложным, чтобы указать, отображается ли память по адресуX преуспел.
  3. Если программа A получает какие-либо ложные ответы, перейдите к # 1.
  4. Программа A отправляет сообщение другим программам, сообщая им, что адрес был проверен и, возможно, использован.
  5. Если новое приложение интересуется данными, оно должно уведомить программу А о том, что ему нужен адрес.
  6. Программа А затем должна сказать всем другим программам прекратить использование данных и перейти к # 1.

Чтобы выяснить, какие адреса А должен предлагать, вы можете иметь карту А с нефиксированным сегментом памяти, посмотреть, по какому адресу он сопоставлен, и предложить этот адрес.Если это неудовлетворительно, сопоставьте другой сегмент и предложите его.В какой-то момент вам понадобится отключить отображение сегментов, но вы не сможете сразу же отобразить их, потому что если вы удалите карту, то переназначите сегмент с одинаковым размером, и вероятность того, что ОС будет снова и снова возвращать вам один и тот же адрес. Имейте в виду, что вы никогда не достигнете консенсуса ;нет никакой гарантии, что во всех процессах имеется достаточно большой сегмент в общем месте.Это может произойти, если все ваши программы независимо используют почти всю память, скажем, если они зарезервированы тонной подкачки (хотя, если вы заботитесь о производительности, чтобы использовать общую память, надеюсь, вы избегаете подкачки).

Все вышеперечисленное предполагает, что вы находитесь в относительно ограниченном адресном пространстве. Если вы используете 64-разрядную версию, это может сработать . Перестановка оперативной памяти и памяти большинства компьютеров будет намного меньше, чем разрешено для 64-разрядных систем, поэтому вы можете поместить карту памяти в очень далеко фиксированный адрес, который вряд ли все процессы уже сопоставили. Я предлагаю по крайней мере 2 ^ 48, так как нынешние 64-битные процессоры x86 не выходят за пределы этого диапазона (несмотря на то, что указатели являются 64-битными, вы можете подключить только столько ОЗУ, сколько позволено 48-битным, но все равно тонна на время написания) Хотя нет никаких причин, по которым умный распределитель кучи не мог бы воспользоваться обширностью адресного пространства, чтобы сократить свою работу по ведению бухгалтерского учета, поэтому, чтобы быть по-настоящему надежным, вам все равно нужно будет достичь консенсуса. Имейте в виду, что вы, по крайней мере, захотите, чтобы адрес был настраиваемым - даже если в ближайшее время у нас не будет такого большого количества памяти, между этим и тем временем кто-то другой может иметь ту же идею и выбрать ваш адрес.

Для двунаправленной связи вы можете использовать любой из сокетов, каналов или другой сегмент совместно используемой памяти. Ваша ОС может предоставлять другие формы IPC. Но строго учтите, что вы, вероятно, сейчас вводите больше сложности, чем вам бы пришлось столкнуться, если бы вы просто использовали межпроцессные контейнеры Boost;)

3 голосов
/ 09 мая 2011

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

1 голос
/ 25 марта 2015

Мое решение:

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

Предупреждения: я использую 64-битную Fedora 21 с Kdevelop 4.7 и считаю, что длина 'void *' составляет 64 бита.Запись на диск адреса заголовка сегмента включает в себя sprintf (bu, "% p", указатель);и написание текстового файла:

Recovery читает этот файл и декодирует шестнадцатеричное число как значение long long.Это возвращается вызывающей стороне, где оно преобразуется как (void *)

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

Дэвид Н Лэйн

1 голос
/ 03 января 2014

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

Вы выбрали http://reversingonwindows.blogspot.sg/2013/12/hardcoded-pointers.html в качестве примера того, как сделать программное обеспечение менее безопасным, минуя ASLR.2-й плохой пример - в бустовой библиотеке .

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

...