在哪种情况下可以使用指针闪烁?

时间:2018-06-25 17:33:39

标签: c++ pointers boost ipc interprocess

我刚刚了解了指针旋转的问题,我不确定它的实际用法。

例如,假设我有一个Windows服务,该服务使用某种指针刷新来序列化包含指针的对象,然后在不同的过程中反序列化它。

它起作用的前提是什么?

在我看来,这将失败,因为指针试图访问的地址在另一个进程的内存空间中,并且操作系统将不允许新进程访问它们。指针混乱使指针在上下文的变化中幸存下来,但这还不够,不是吗?首先,还需要将数据放在一种共享内存段上,对吗?

如果您碰巧知道C ++中的任何带有boost之类的库的东西,我也很好奇。

1 个答案:

答案 0 :(得分:0)

当指针混乱时,直到它们变得不混乱时,才能有效地跟随它们。

想象一下,如果您有一堆记录,每个记录都带有一些任意图中的其他记录的链接(指针)。

一种处理这些错误的幼稚方法是将指针的二进制值作为UID并将其序列化。在执行此操作的同时,我们维护一个记录地址表,以按顺序进行序列化,然后最后对它进行序列化。将此表称为“毛毛雨表”。

反序列化时,我们加载数据结构,并建立一个表(序列化顺序)到(存储器中的新记录地址)。然后,我们加载swizzle表,该表是从(旧地址)到(序列化顺序)的映射。

我们合并这两个表,并得到一个(旧地址)到(内存中的新记录地址)表-不混乱的表。

接下来,我们遍历反序列化记录,并为每个指针应用此映射。每个地址的旧二进制值存储在某个指针中。我们在取消收据表中查看它,然后将其替换。现在,每个指针都指向新地址空间中记录的地址。

struct node {
  std::vector<node*> links;
  void write( OutArch& out ) const& {
    out.register_swizzle(this);
    out << links.size();
    for (node* n:links) {
      out << out.swizzle(n);
    }
  }
  static node* read( InArch& in ) {
    auto* r = new node;
    in.register_unswizzle( r );
    std::size_t n;
    in >> n;
    r->reserve(n);
    for (std::size_t i = 0; i<n; ++i) {
      std::intptr_t ptr;
      in >> ptr;
      r->links.push_back( reinterpret_cast<node*>(ptr) ); // danger
    }
    return r;
  }
  friend void do_unswizzle( InArch& in, node* n ) {
    for (node*& link : n->links ) {
      link = in.unswizzle(link);
    }
  }
};
struct OutArch {
  friend void operator<<( OutArch& arch, std::size_t count ); //TODO
  friend void operator<<( OutArch& arch, std::intptr_t ptr ); //TODO
  std::intptr_t swizzle( void* ptr ) {
    return reinterpret_cast<std::intptr_t>(ptr);
  }
  void register_swizzle( void* ptr ) {
    swizzle_table.insert( {(reinterpret_cast<std::intptr_t>(p), record_number} );
    ++record_number;
  }
private:
  // increased 
  std::size_t record_number = 0;
  std::map< std::intptr_t, std::size_t > swizzle_table;
};
struct InArch {
  friend void operator>>( InArch& arch, std::size_t& count ); //TODO
  friend void operator>>( InArch& arch, std::intptr_t& count ); //TODO
  template<class T>
  void register_unswizzle( T* t ) {
    unswizzle_table.insert( {record_number, t} );
    ++record_number;
    unswizzle_tasks.push_back([t](InArch* self){
      do_unswizzle( *self, t );
    });
  }
  struct unswizzler_t {
    void* ptr;
    template<class T>
    operator T*()&&{return static_cast<T*>(ptr);}
  };
  unswizzler_t unswizzle( void* ptr ) {
    auto p = reinterpret_cast<std::intptr_t>(ptr);
    auto it1 = swizzle_table.find(p);
    if (it1 == swizzle_table.end()) return {nullptr};
    auto it2 = unswizzle_table.find(it1->second);
    if (it2 == unswizzle_table.end()) return {nullptr};
    return { it2->second };
  }
  void load_swizzle_table(); //TODO
  void execute_unswizzle() {
    for (auto&& task: unswizzle_tasks) {
      task(this);
    }
  }
private:
  // increased 
  std::size_t record_number = 0;
  std::map< std::size_t, void* > unswizzle_table;
  std::map< std::intptr_t, std::size_t > swizzle_table;
  std::vector< std::function< void(InArch*) > > unswizzle_tasks;
};

有很多方法可以解决。您可以保存将序列化它的顺序(例如),而不是保存指针的二进制值。但这需要一些仔细的预处理或时间旅行,因为您将引用尚未序列化的结构。

或者您可以生成一个Guid,在每条记录中写出该Guid,并在旧过程中将{record address}的一个混乱表保留到{guid}。保存记录时,您会看到指针是否在摇表中。如果没有,请添加它们。然后编写guid而不是指针。在这种情况下,请不要写表格表。 {guid}到{record address}的不混乱表可以仅从每个记录上的guid标头构造。然后,使用该取消转换表在目标端重建记录。

相关问题