在啥情况下可以使用指针混合?

Posted

技术标签:

【中文标题】在啥情况下可以使用指针混合?【英文标题】:In which context can pointer swizzling be used?在什么情况下可以使用指针混合? 【发布时间】:2018-06-25 17:33:38 【问题描述】:

我刚刚了解了指针调配,但我不太确定它的实际用途。

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

它起作用的前提条件是什么?

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

如果你碰巧知道 C++ 中的任何库,如 boost 或其他类似的库,我也很想看看实际的例子。

【问题讨论】:

***.com/questions/21218304/what-is-pointer-swizzling 指针调配的重点不是将指针从一个进程转移到另一个进程。它是以一种可以在其他进程中重新创建指针的方式传输包含指针的数据。还必须传输所有数据。 @super 好吧,我想我明白了。这意味着一些包含指针的随机数据使用任何随机方法(套接字、队列、共享内存......)从一个进程传输到另一个进程,但在序列化过程中,指针被转换为其他东西,以便在之后重新创建它们其他内存空间中的数据传输。所以指针调配只是指针序列化的一种特殊情况,仅此而已。 Boost Interprocess 为此使用了offset_ptr。它可以隐藏编程界面中的所有机制 【参考方案1】:

当你有混合指针时,你不能(有效地)跟随它们,直到它们被取消混合。

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

混合这些的一种天真的方法是将指针的二进制值作为 UID,并将其序列化。当我们这样做时,我们维护了一个记录地址表以在序列化中排序,并且我们最后序列化它。将其称为调酒桌。

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

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

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

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 的 swizzle 表。当您保存记录时,您会看到指针是否在您的调酒表中;如果没有,请添加它们。然后写 guid 而不是指针。在这种情况下不要写调酒表; guid 到 record address 的 unswizzle 表可以仅从每条记录上的 guid 标头构建。然后使用那个 unswizzle 表,在目标端重建记录。

【讨论】:

我可以完美地看到指针混合的意义以及它如何与您的示例一起使用:D 非常感谢!

以上是关于在啥情况下可以使用指针混合?的主要内容,如果未能解决你的问题,请参考以下文章

我可以混合使用 RKEntityMapping 和 RKObjectMapping

如何在没有javascript的情况下使用css混合依赖于内容和百分比高度/宽度[重复]

在不混合的情况下使不透明的 UIView 变暗

hive 动态分区与混合分区

Office365混合部署实战系列教程二:制定混合部署的迁移方案

混合安全和不安全的渠道