通过内存共享C++对象

4

我有一个与进程间通信相关的问题,希望熟悉此类技术的人能够回答。

情况

  • 我有一个程序(程序A),我可以在其中添加一些代码,但非常有限。这是生成大量数据的主要程序。
  • 数据格式受限,因此我想创建第二个程序(程序B),因此需要从A获取数据到B,有时甚至需要使A运行一些没有返回值的函数。
  • 我知道命名管道,但我觉得它们可能会很笨重?虽然我有以下担忧(可能是没有根据的):
    1. 数据流 => 转换为二进制 -> 将数据放入内存 -> 服务器读取 -> 转换为字符串 -> 通过可能的 switch 语句确定所请求的内容 -> 获取请求内容 -> 转换为二进制 -> 放入内存 -> 客户端读取并转换为字符串 / 一些可接受的格式。
    2. 必须在两侧使用基本的 switch 语句,如果您想要除字符串以外的其他信息格式,则需要考虑此问题。
    3. 一个消息可能必须等待另一个消息完成,因此在同时进行大量调用时可能会变慢?虽然我不确定。
  • 其他进程间通信方法可能存在相同的问题。
  • 我认为更好的解决方案是创建一个“对象” - 类,并在程序之间共享对象内存地址,从而在理论上“合并”A和B:

    1. 没有编码和解码问题等问题
    2. 通过调用函数简单请求/调用数据。
    3. 这个函数返回正确类型,无需确定正确类型(即bool / int / string / double等)

    我明白这也有几个问题,例如如果另一个程序访问它,则对象可能会从内存位置中删除。

问题

  • 如何解决这些问题:
  • 在C++中是否有一种调用选项,可以允许我从内存地址中读写数据?目前:
    1. 我可以在A和B之间访问同一个对象,但我无法写入/读取,因为那样会引发异常。所以我是否可以通过简单的调用来读/写此对象?
    2. 我知道WriteProcessMemory函数-但这不是我想要的-即我不一定想改变内存值,只是从B访问数据/调用A执行的操作。
  • 有没有简单易行的方法?我知道有一个叫做Boost的东西,但对此一无所知-这是我的最佳选择吗?->即我应该将其作为最佳解决方案来调查吗?

非常感谢您提前对此问题的任何建议。


"我知道有一个叫做boost的东西。很好,但是最好的方法是使用它:http://boost.org。还有……惊喜!有一个东西可以帮助你解决问题:[Boost.Interprocess](http://www.boost.org/doc/libs/1_55_0/doc/html/interprocess.html)" - Mark Garcia
你可以通过管道轻松地发送二进制数据。实际上,对于操作系统来说,通过管道传输的是原始字节流。但是,你最好使用适当的序列化格式。如何使用“Cap'n Proto”?它有一个优点,就是线路格式与内存中的数据相同,非常适合你的情况。 - datenwolf
3个回答

1

Boost.Interprocess有很多种方法可以在进程之间共享数据,其中一种是shared_memory

这个例子来自于boost,其中一个程序作为同一内存对象的服务器或客户端(取决于是否给出参数)

include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>
#include <cstring>
#include <cstdlib>
#include <string>

int main(int argc, char *argv[])
{
   using namespace boost::interprocess;

   if(argc == 1){  //Parent process
      //Remove shared memory on construction and destruction
      struct shm_remove
      {
         shm_remove() { shared_memory_object::remove("MySharedMemory"); }
         ~shm_remove(){ shared_memory_object::remove("MySharedMemory"); }
      } remover;

      //Create a shared memory object.
      shared_memory_object shm (create_only, "MySharedMemory", read_write);

      //Set size
      shm.truncate(1000);

      //Map the whole shared memory in this process
      mapped_region region(shm, read_write);

      //Write all the memory to 1
      std::memset(region.get_address(), 1, region.get_size());

      //Launch child process
      std::string s(argv[0]); s += " child ";
      if(0 != std::system(s.c_str()))
         return 1;
   }
   else{
      //Open already created shared memory object.
      shared_memory_object shm (open_only, "MySharedMemory", read_only);

      //Map the whole shared memory in this process
      mapped_region region(shm, read_only);

      //Check that memory was initialized to 1
      char *mem = static_cast<char*>(region.get_address());
      for(std::size_t i = 0; i < region.get_size(); ++i)
         if(*mem++ != 1)
            return 1;   //Error checking memory
   }
   return 0;
}

谢谢Pieter。我会尝试这个。我以前没有做过共享内存。我的想法正确吗?你可以将一个对象放在共享内存位置,并且只要A和B都具有对象构造的“原型”——即两者都引用相同的类,并仅通过在两侧创建指针即可使用它?还是更像“命名管道”,其中内存被赋予名称,A和B都观察共享内存中的更改。我认为它是前者。我将进一步调查Boost。谢谢! - DaClan
2
@DaClan 可以在共享内存中创建类,但是需要注意同步问题,请参考 http://www.boost.org/doc/libs/1_55_0/doc/html/interprocess/managed_memory_segments.html#interprocess.managed_memory_segments.managed_memory_segment_features.allocation_types (最好花些时间阅读整个Interprocess文档,这不是一项微不足道的任务)。 - Pieter

1

简化!

通常,共享状态可能是一个不好的主意,不易测试,并且可能会像全局变量一样破坏难以管理的架构。

ØMQ指南可能会帮助您掌握可能的解决方案。一般来说,如果没有特殊要求,请使用消息传递。ØMQ“套接字”速度快,相当稳定,并且需要非常少的代码即可启动。

更新:现成的:msgpack-rpcprotobuf-remote


谢谢@Dmitry。我知道这个解决方案。但除非我错了,否则你仍然会面临编码和解码的问题,也就是说有100个函数要调用,因此您将不得不确定实际被发送的消息调用了哪个函数——我以前尝试过类似的命名管道,但编写编码和解码函数似乎很麻烦,因为您需要在服务器和客户端两侧编写编码和解码函数。这可能是一个简单的解决方案!但它是否真的快速/高效地编写呢?难道一个更好的解决方案不是共享类吗? - DaClan
有简单的解决方案:RPC:msgpack-rpcprotobuf-remote - Dmitry Ledentsov
只要有适当的关注点分离,您就不需要编写编码和解码函数。您接口的声明将同时被编码和解码的实现以及C++代码使用。 - Dmitry Ledentsov
1
Cap'n Proto - RPC - datenwolf
@datenwolf 我对 Cap'n Proto 完全不熟悉。我也会进行调查。谢谢! - DaClan

0
你是在为客户开发软件,还是只是为了数据分析而自己写的?如果只是为了自己,你可以将数据写入硬盘,并将分析分成两个步骤。

这是一个测试项目。如果可能的话,它可以被出售。主要是为我所在的金融公司而设计的。程序A是一个第三方程序,允许添加C++代码到有限的程度。由于数据量太大,无法全部写入硬盘。程序A大约有100个函数,主要调用另一个位置的服务器(即存储在离线位置的数据)。我已经要求API函数,但他们不愿直接给我访问权限。因此,程序A与服务器C通信...复制所有数据将行不通,很遗憾。 - DaClan
Ps. 获取这些数据具有关键的时间影响。因此,我的解决方案必须既易于实现(例如,不想使用消息传递,因为那将需要编写每个函数两次 - 一次用于消息 - 另一次用于解码方面理解应该调用哪个函数),又快速。 - DaClan
@DaClan,我对进程间通信不是很熟悉。使用TCP连接怎么样?您可以将您的程序作为服务器,将程序A中的东西作为客户端。 - jiandingzhe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接