Boost Interprocess: управляемые ошибки общей памяти - PullRequest
0 голосов
/ 07 мая 2018

Я использую межпроцессную библиотеку бустов для разделения памяти между несколькими программами. 3 или 4 другие программы будут читать и записывать содержимое общей памяти в cust_order. Доступ к пространству памяти должен быть сериализован.

В приведенном ниже примере программы движок перебирает содержимое параметра risk_queue и, если он заполнен, берет первый номер cust_order и находит этот заказ для обработки.

  • в заголовочном файле objects.h, используемом каждой программой:

    struct cust_order {
      int ID;
      char CLID[128];
      int CUST_ID;
      char ORDER_STATUS;
    };
    
    cust_order o;
    
    boost::interprocess::managed_shared_memory
        cust_order_segment(boost::interprocess::open_or_create, "cust_order",
                           65536 * 100);
    
    typedef int cust_order_KeyType;
    typedef cust_order cust_order_MappedType;
    typedef std::pair<const int, cust_order> cust_order_ValueType;
    
    typedef boost::interprocess::allocator<
        cust_order_ValueType,
        boost::interprocess::managed_shared_memory::segment_manager>
        cust_order_ShmemAllocator;
    
    typedef boost::interprocess::map<cust_order_KeyType, cust_order_MappedType,
                                     std::less<cust_order_KeyType>,
                                     cust_order_ShmemAllocator>
        cust_order_MySHMMap;
    cust_order_MySHMMap::iterator cust_order_iter;
    
    boost::interprocess::managed_shared_memory
        risk_queue_segment(boost::interprocess::open_or_create, "risk_queue",
                           65536 * 100);
    typedef int risk_queue_KeyType;
    typedef int risk_queue_MappedType;
    typedef std::pair<const int, int> risk_queue_ValueType;
    typedef boost::interprocess::allocator<
        risk_queue_ValueType,
        boost::interprocess::managed_shared_memory::segment_manager>
        risk_queue_ShmemAllocator;
    
    typedef boost::interprocess::map<risk_queue_KeyType, risk_queue_MappedType,
                                     std::less<risk_queue_KeyType>,
                                     risk_queue_ShmemAllocator>
        risk_queue_MySHMMap;
    risk_queue_MySHMMap::iterator risk_queue_iter;
    
  • в engine.cpp:

     int main() {
    
         risk_queue_ShmemAllocator risk_queue_alloc_inst(
                 risk_queue_segment.get_segment_manager());
    
         cust_order_ShmemAllocator cust_order_alloc_inst(
                 cust_order_segment.get_segment_manager());
    
         for (; 0 < 1;) {
    
             boost::interprocess::offset_ptr<risk_queue_MySHMMap> risk_queue_m_pmap =
                 risk_queue_segment.find<risk_queue_MySHMMap>("risk_queue").first;
    
             boost::interprocess::offset_ptr<cust_order_MySHMMap> cust_order_m_pmap =
                 cust_order_segment.find<cust_order_MySHMMap>("cust_order").first;
    
             risk_queue_iter = risk_queue_m_pmap->begin();
             if (risk_queue_iter != risk_queue_m_pmap->end()) {
                 ordid = risk_queue_iter->second;
                 cust_order_m_pmap->find(ordid)->second = o;
                 o.ORDER_STATUS = '0';
                 o = cust_order_m_pmap->find(ordid)->second;
                 risk_queue_m_pmap->erase(ordid);
             }
         };
         return 0;
     }
    

Это ошибка, которую я получаю после нескольких секунд работы программ:

engine: /usr/include/boost/intrusive/bstree.hpp:1331: boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::iterator boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::insert_unique_commit(boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::reference, const insert_commit_data&) [with ValueTraits = boost::intrusive::bhtraits<boost::container::container_detail::tree_node<std::pair<const int, event>, boost::interprocess::offset_ptr<void>, (boost::container::tree_type_enum)0, true>, boost::intrusive::rbtree_node_traits<boost::interprocess::offset_ptr<void>, true>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 3>; VoidOrKeyOfValue = void; VoidOrKeyComp = boost::container::value_to_node_compare<boost::container::container_detail::tree_node<std::pair<const int, event>, boost::interprocess::offset_ptr<void>, (boost::container::tree_type_enum)0, true>, boost::intrusive::tree_value_compare<boost::interprocess::offset_ptr<std::pair<const int, event>, long int, long unsigned int, 0>, std::less<int>, boost::container::container_detail::select1st<int>, false> >; SizeType = long unsigned int; bool ConstantTimeSize = true; boost::intrusive::algo_types AlgoType = (boost::intrusive::algo_types)5; HeaderHolder = void; boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::iterator = boost::intrusive::tree_iterator<boost::intrusive::bhtraits<boost::container::container_detail::tree_node<std::pair<const int, event>, boost::interprocess::offset_ptr<void>, (boost::container::tree_type_enum)0, true>, boost::intrusive::rbtree_node_traits<boost::interprocess::offset_ptr<void>, true>, (boost::intrusive::link_mode_type)0, boost::intrusive::dft_tag, 3>, false>; boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::reference = boost::container::container_detail::tree_node<std::pair<const int, event>, boost::interprocess::offset_ptr<void>, (boost::container::tree_type_enum)0, true>&; boost::intrusive::bstree_impl<ValueTraits, VoidOrKeyOfValue, VoidOrKeyComp, SizeType, ConstantTimeSize, AlgoType, HeaderHolder>::insert_commit_data = boost::intrusive::insert_commit_data_t<boost::interprocess::offset_ptr<boost::intrusive::compact_rbtree_node<boost::interprocess::offset_ptr<void> >, long int, long unsigned int, 0> >]: Assertion `( p == this->end() || !this->comp()(*p, value) )' failed.
Aborted (core dumped)

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

1 Ответ

0 голосов
/ 08 мая 2018

Хорошо, я просмотрел ваш код. Вы сказали это сами:

Доступ к пространству памяти должен быть сериализован.

Так почему бы и нет?

Примечания

Упрощение заголовка:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/containers/map.hpp>

struct cust_order {
    int  ID;
    char CLID[128];
    int  CUST_ID;
    char ORDER_STATUS;
};

namespace bip = boost::interprocess;

namespace Shared {
    using Segment = bip::managed_shared_memory;
    using Manager = Segment::segment_manager;
    template <typename T> using Alloc = bip::allocator<T, Manager>;

    template <typename K, typename V, typename Cmp = std::less<K> >
        using Map = bip::map<K, V, Cmp, Alloc<typename bip::map<K, V>::value_type> >;
}

using OrderTable = Shared::Map<int, cust_order>;
using RiskQueue = Shared::Map<int, int>;

Упрощение основного:

int main() {
    Shared::Segment cust_order_segment(bip::open_or_create, "cust_order", 65536 * 100);
    Shared::Segment risk_queue_segment(bip::open_or_create, "risk_queue", 65536 * 100);

    auto risk_queue  = risk_queue_segment.find_or_construct<RiskQueue>("risk_queue")(risk_queue_segment.get_segment_manager());
    auto order_table = cust_order_segment.find_or_construct<OrderTable>("cust_order")(cust_order_segment.get_segment_manager());

    while (true) {
        while (!risk_queue->empty()) {

            auto it    = risk_queue->begin();
            auto ordid = it->second;

            order_table->at(ordid).ORDER_STATUS = '0';
            risk_queue->erase(ordid);
        }
    }
}

Наиболее заметные вещи:

  1. синхронизации вообще нет (за исключением некоторых деталей внутри менеджера сегментов)
  2. вы не проверяли на наличие ошибок (отсутствующие сегменты, отсутствующие объекты, отсутствующий порядок, на который есть ссылка в risk_queue)
  3. почему структуры данных в отдельных сегментах?
  4. почему вы работаете в узком цикле, как будто имеете дело со структурами данных без блокировки?
  5. почему вы злоупотребляете картами, как если бы они были приоритетными очередями? Интерфейс просто не соответствует предполагаемому использованию
  6. Наиболее интересно, поскольку RiskQueue упорядочен по ключу и рассматривается как очередь, мы должны предположить, что тип ключа указывает на риск / приоритет.

    Отображенный тип, безусловно, является идентификатором заказа, который ссылается на таблицу заказов клиентов. Пока все хорошо.

    Но тогда вы делаете

        risk_queue->erase(ordid);
    

    Это полностью сбивает с толку, потому что это обрабатывает идентификатор заказа, как если бы он был ключом к "очереди риска"? В очередях нет ключей, мы только что пришли к выводу, что «ключ» риска указывает на приоритет (поэтому это не идентификатор заказа).

    Даже предположение, что это должно было быть order_table->erase(ordid), на самом деле не имеет особого смысла, потому что тогда какова цель установки статуса заказа прямо перед удалением.

    Я могу только заключить, что строка была ошибкой и должна была быть

        risk_queue->erase(it);
    

Конечно, все это не имеет значения, пока не будет решена синхронизация.

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

Если вы действительно ДОЛЖНЫ иметь доступ к данным без блокировки, убедитесь, что структуры данных являются фактически свободными от блокировки, а также убедитесь, что вы поддерживаете согласованность (это проблема hard , так как вы решили разделить данные на две структуры).

...