Увеличить IPC managed_memory_segment bad_alloc - PullRequest
0 голосов
/ 18 января 2019

При попытке записать часть (переносимого) кода C ++, который использует shared_memory_segment для записи в «разделяемую память», я столкнулся с boost::interprocess::bad_alloc несколько раз.Из Boost документации :

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

Итак, я должен выделять слишкоммало памяти.Далее следует код (только для записи, поскольку чтение здесь не имеет значения):

shared_memory.h:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>

#include <string>
#include <exception>

namespace my_shared_memory
{
  typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
  typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> IPCString;
  typedef boost::interprocess::allocator<IPCString, boost::interprocess::managed_shared_memory::segment_manager> StringAllocator;
  typedef boost::interprocess::vector<IPCString, StringAllocator> ShmVector;

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data) ;

  const std::string shm_prefix = "shm_";
  const std::string mutex_prefix = "mtx_";
}

shared_memory.cpp:

#include "shared_memory.h"

namespace apl_shared_memory
{

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data)
  {
    bool ret_val;
    std::string shm_name = shm_prefix + wsuid;
    std::string mtx_name = mutex_prefix + wsuid;
    boost::interprocess::named_mutex named_mtx{boost::interprocess::open_or_create, mtx_name.c_str()};
    size_t size = (sizeof(loop_val) + loop_val.size() + sizeof(should_intercept) + should_intercept.size() + sizeof post_data + post_data.size()) * 5;

    try
    {
      named_mtx.lock();
      boost::interprocess::shared_memory_object::remove(shm_name.c_str());
      boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shm_name.c_str(), size);
      CharAllocator     charallocator  (segment.get_segment_manager());
      StringAllocator   stringallocator(segment.get_segment_manager());

      IPCString shm_loop_val(charallocator);
      IPCString shm_should_intercept(charallocator);
      IPCString shm_intercepted_data(charallocator);
      shm_loop_val = loop_val.c_str();
      shm_should_intercept = should_intercept.c_str();
      shm_intercepted_data = post_data.c_str();

      segment.destroy<ShmVector>("ShmVector");
      ShmVector *shmVector = segment.construct<ShmVector>("ShmVector")(stringallocator);
      shmVector->clear();
      shmVector->push_back(shm_loop_val);
      shmVector->push_back(shm_should_intercept);
      shmVector->push_back(shm_intercepted_data);

      named_mtx.unlock();
      ret_val = true;
    } catch(const std::exception& ex)
    {
      ret_val = false;
      named_mtx.unlock();
      boost::interprocess::shared_memory_object::remove(shm_name.c_str());
    }

    named_mtx.unlock();
    return ret_val;
  }

}

Да, я понимаю, мне не нужны unlock звонки во всех трех местах.

Кажется, проблема в линии:

size_t size = (sizeof(loop_val) + loop_val.size() + sizeof(should_intercept) + should_intercept.size() + sizeof post_data + post_data.size()) * 5;

Я думал, что было бы излишне прибавлять времена 5, но это, очевидно, не тот случай.Этот код отлично работает на Debian 9 с gcc 6.3.0 и Windows 10 с сообществом Microsoft Visual Studio 2015.Однако на MacOS с Xcode 10.1 я получаю boost::interprocess::bad_alloc, когда пытаюсь вставить хотя бы первый элемент в вектор.Кажется, что решение умножает размер на 10 вместо 5, но это просто кажется расточительным.

Я добавил segment.get_free_memory вызовов и определил, что для вектора, который содержит следующие три строки (без кавычек)

"true" "true" ""

Мне нужно 512 байт с Xcode.Оператор sizeof вернул 32, 24 и 32 для IPCString, std::string и ShmVector соответственно.Поэтому, похоже, мне нужно 32+4 байтов для одной "истинной" строки и 32 для пустой строки.При добавлении ShmVector мне нужно 36+36+32+32=136 байтов для самих структур.Я вычислил размер, используя sizeof(std::string), который здесь равен 24 (недосмотр), так что я получаю (28+28+24)*5 = 400, чего здесь недостаточно.

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

РЕДАКТИРОВАТЬ: Я изменил размер на:

size_t size = (sizeof(IPCString) + loop_val.size() + sizeof(IPCString) + should_intercept.size() + sizeof(IPCString) + post_data.size() + sizeof(ShmVector)) * 2 + 500;

Пока все хорошо.У меня всегда меньше 1060 * байт свободного места после того, как я закончу запись в сегмент.Я пробовал с различными размерами данных, от менее 100 байтов до 3 мегабайт.Я попытался без умножения на 2, но в этом случае, когда я пытаюсь вставить большие блоки данных, программа вылетает, так как 500 дополнительные байты не обеспечивают достаточную свободу действий.

Если нетответ через пару дней, я выложу эту правку как ответ.

Ответы [ 2 ]

0 голосов
/ 23 января 2019

После некоторых исследований и испытаний я нашел решение, приведенное ниже. Ключевым моментом здесь является использование boost::move (или, возможно, std :: move, не знаю, будет ли здесь разница). Поскольку мы используем boost::move, не будет копий IPCString объектов и, следовательно, не нужно умножать на 2. Помните, из-за того, как мы определили IPCString, он будет размещен внутри сегмента разделяемой памяти.

Необходимы дополнительные издержки (возможно, из-за выравнивания или других добавленных накладных расходов, или обоих), но всегда остается некоторое пространство. Поскольку я мог послать несколько мегабайт, издержки 500 байтов довольно малы.

Я использую std::string метод size(), потому что он возвращает размер строки в байтах .

Код следует:

shared_memory.h:

#pragma once

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>

#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>

#include <string>
#include <vector>
#include <exception>

namespace apl_shared_memory
{
  typedef boost::interprocess::allocator<char, boost::interprocess::managed_shared_memory::segment_manager> CharAllocator;
  typedef boost::interprocess::basic_string<char, std::char_traits<char>, CharAllocator> IPCString;
  typedef boost::interprocess::allocator<IPCString, boost::interprocess::managed_shared_memory::segment_manager> StringAllocator;
  typedef boost::interprocess::vector<IPCString, StringAllocator> ShmVector;

  bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data) ;


  const std::string shm_prefix = "shm_";
  const std::string mutex_prefix = "mtx_";
}

shared_memory.cpp:

#include "shared_memory.h"

#include "logger.h"



namespace apl_shared_memory

{
    bool write_to_memory(std::string wsuid, std::string loop_val, std::string should_intercept, std::string post_data)
    {
        bool ret_val;
        std::string shm_name = shm_prefix + wsuid;
        std::string mtx_name = mutex_prefix + wsuid;

        boost::interprocess::named_mutex named_mtx{ boost::interprocess::open_or_create, mtx_name.c_str() };

        // Add the size of all the structures we're putting in the shared memory region and add some for the overhead.
        size_t size = (3*sizeof(IPCString) + loop_val.size() + should_intercept.size() + post_data.size() + sizeof(ShmVector)) + 500;

        try
        {
            named_mtx.lock();
            boost::interprocess::shared_memory_object::remove(shm_name.c_str());
            boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, shm_name.c_str(), size);

            CharAllocator     charallocator(segment.get_segment_manager());
            StringAllocator   stringallocator(segment.get_segment_manager());

            IPCString shm_loop_val(charallocator);
            IPCString shm_should_intercept(charallocator);
            IPCString shm_intercepted_data(charallocator);

            shm_loop_val = loop_val.c_str();
            shm_should_intercept = should_intercept.c_str();
            shm_intercepted_data = post_data.c_str();

            segment.destroy<ShmVector>("ShmVector");
            ShmVector *shmVector = segment.construct<ShmVector>("ShmVector")(stringallocator);
            shmVector->clear();
            shmVector->reserve(3);

            // push_back will call a copy-constructor. But, if we send a rvalue reference (i.e. if we move it), there will be no copying.
            shmVector->push_back(boost::move(shm_loop_val));
            shmVector->push_back(boost::move(shm_should_intercept));
            shmVector->push_back(boost::move(shm_intercepted_data));

            ret_val = true;
        }

        catch (const std::exception& ex)

        {
            ret_val = false;
            boost::interprocess::shared_memory_object::remove(shm_name.c_str());
        }

        named_mtx.unlock();

        return ret_val;
    }
}

Если кто-то считает, что я сделал что-то не так, пожалуйста, прокомментируйте.

0 голосов
/ 22 января 2019

Вы делаете много предположений об эквивалентности размеров шрифта. Использование памяти строки ac не совпадает с использованием памяти std :: string, которое не совпадает с использованием памяти IPCString, которая на самом деле является vector , так что вас это не интересует по размеру или длине, но вместимости.

Размер ваших компонентов изо рта лошади: возьмите sizeof из IPCString переменных вместо std::string переменных, а также shm_loop_val.capacity() + shm_should_intercept.capacity() + shm_intercepted_data.capacity() вместо определения размера std::strings!

Вы должны быть ближе с:

size_t size = (3 * sizeof(IPCString) + sizeof(ShmVector) + shm_loop_val.capacity() +
              shm_should_intercept.capacity() + shm_intercepted_data.capacity();

Реализация Boost может также выровнять слова по вашим данным, но я начинаю понимать, что это очень маловероятно. Это выглядит как сериализованное. Мы можем проверить, что у вас все еще есть проблемы.

...