首页 > 解决方案 > 如何使用 Boost library 1.55.0 使用 matlab 应用程序创建共享内存

问题描述

我正在尝试使用 boost library 1.55.0 来创建共享内存。我有一个 example.cpp 文件来创建 boost 共享内存。这个文件Mexing成功了,但是在调试“MATLAB.exe已经触发了断点”的时候抛出了下面的异常,这个异常是不是因为boost的版本和matlab版本不兼容?如何解决这个
`/* 文件:sfun_counter_cpp.cpp * 摘要:
*
* 将 C++ 对象存储在
指针向量 PWork 中的 C++ S 函数示例。
*
* 版权所有 1990-2000 The MathWorks, Inc.
*

*/

#include "iostream"   
#include <boost/interprocess/managed_shared_memory.hpp> 
#include <boost/interprocess/containers/vector.hpp>  
#include <boost/interprocess/allocators/allocator.hpp>

typedef struct
{
 int           outGate;
 unsigned int  outPin;
 int           inGate;
 unsigned int  inPin;   
} wire;

typedef struct
{
unsigned int  gateType;
unsigned int  inPins;
unsigned int  outPins;
std::vector<wire>  inWires;
std::vector<wire>  outWires;
} gate;

std::vector<gate> gates;
wire wiredata;
gate gatedata;

class  counter {
    double  x;
public:
    counter() {
        x = 0.0;
    }
    double output(void) {
        x = x + 1.0;
        return x; 
    }
};

#ifdef __cplusplus
extern "C" { // use the C fcn-call standard for all functions  
#endif       // defined within this scope                     

#define S_FUNCTION_LEVEL 2
#define S_FUNCTION_NAME  sfun_counter_cpp

/*
* Need to include simstruc.h for the definition of the SimStruct and
* its associated macro definitions.
*/
#include "simstruc.h"

/*====================*
* S-function methods *
*====================*/

/* Function: mdlInitializeSizes ===============================================
* Abstract:
*    The sizes information is used by Simulink to determine the S-function
*    block's characteristics (number of inputs, outputs, states, etc.).
*/
static void mdlInitializeSizes(SimStruct *S)
{
    /* See sfuntmpl_doc.c for more details on the macros below */

    ssSetNumSFcnParams(S, 1);  /* Number of expected parameters */
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        /* Return if number of expected != number of actual parameters */
        return;
    }

    ssSetNumContStates(S, 0);
    ssSetNumDiscStates(S, 0);

    if (!ssSetNumInputPorts(S, 0)) return;

    if (!ssSetNumOutputPorts(S, 1)) return;
    ssSetOutputPortWidth(S, 0, 1);

    ssSetNumSampleTimes(S, 1);
    ssSetNumRWork(S, 0);
    ssSetNumIWork(S, 0);
    ssSetNumPWork(S, 1); // reserve element in the pointers vector
    ssSetNumModes(S, 0); // to store a C++ object
    ssSetNumNonsampledZCs(S, 0);

    ssSetOptions(S, 0);
}

/* Function: mdlInitializeSampleTimes 
=========================================
* Abstract:
*    This function is used to specify the sample time(s) for your
*    S-function. You must register the same number of sample times as
*    specified in ssSetNumSampleTimes.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, mxGetScalar(ssGetSFcnParam(S, 0)));
    ssSetOffsetTime(S, 0, 0.0);

}

#define MDL_START  /* Change to #undef to remove function */
#if defined(MDL_START) 
/* Function: mdlStart 
=======================================================
* Abstract:
*    This function is called once at start of model execution. If you
*    have states that should be initialized once, this is the place
*    to do it.
*/
static void mdlStart(SimStruct *S)
{
    ssGetPWork(S)[0] = (void *) new counter; // store new C++ object in 
the
}                                            // pointers vector
#endif /*  MDL_START */

/* Function: mdlOutputs =======================================================
* Abstract:
*    In this function, you compute the outputs of your S-function
*    block. Generally outputs are placed in the output vector, ssGetY(S).
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{

    using namespace boost::interprocess;
    counter *c = (counter *) ssGetPWork(S)[0];   // retrieve C++ object from
    shared_memory_object::remove("MySharedMemory");
    //create the shared memory 
    managed_shared_memory segment(create_only, "MySharedMemory", 65536); 

    //create the allocators for the struct elements to be accessed as 
vectors
    typedef allocator<gate, 
managed_shared_memory::segment_manager>gate_alloc;
    typedef allocator<wire, 
managed_shared_memory::segment_manager>inwire_alloc;
    typedef allocator<wire, 
managed_shared_memory::segment_manager>outwire_alloc;

    //create a boost vector with an associated allocator to it
    typedef vector<gate, gate_alloc>gate_vec;
    typedef vector<wire, inwire_alloc>inwire_vec;
    typedef vector<wire, outwire_alloc>outwire_vec;

    //Initialize shared memory STL-compatible allocator
    const gate_alloc alloc_inst(segment.get_segment_manager());
    const inwire_alloc alloc_inst1(segment.get_segment_manager());
    const outwire_alloc alloc_inst2(segment.get_segment_manager());

    //construct the segment for pushing the data into it
    gate_vec *gate_data = segment.construct<gate_vec>("gatedata") 
(alloc_inst);
    inwire_vec *inwire_data = segment.construct<inwire_vec>("inwiredata")  
        (alloc_inst1);
    outwire_vec *outwire_data = segment.construct<outwire_vec> 
("outwiredata") 
        (alloc_inst2);


    //push the data into the vectors
    wiredata.inGate = 10;
    wiredata.inPin = 2;
    wiredata.outGate = 1;
    wiredata.outPin = 3;
    inwire_data->push_back(wiredata);
    outwire_data->push_back(wiredata);

    gatedata.gateType = 1;
    gatedata.inPins = 2;
    gatedata.outPins = 3;
    gate_data->push_back(gatedata);


    real_T  *y = ssGetOutputPortRealSignal(S,0); // the pointers vector 
and use
    y[0] = c->output();                          // member functions of 
the
}                                                // object

/* Function: mdlTerminate 
=====================================================
* Abstract:
*    In this function, you should perform any actions that are necessary
*    at the termination of a simulation.  For example, if memory was
*    allocated in mdlStart, this is the place to free it.
*/
static void mdlTerminate(SimStruct *S)
{
    counter *c = (counter *) ssGetPWork(S)[0]; // retrieve and destroy C++
    delete c;                                  // object in the 
termination
}                                              // function
/*======================================================*
* See sfuntmpl_doc.c for the optional S-function methods *
*======================================================*/

/*=============================*
* Required S-function trailer *
*=============================*/

#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file?*/
#include "simulink.c"      /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"       /* Code generation registration function */
#endif

#ifdef __cplusplus
} // end of extern "C" scope
#endif

` 这是我必须混合、调试和运行的函数。虽然上述代码片段的混合成功,但在调试时会抛出异常“MATLAB.exe 已触发断点”。

标签: matlabboostshared-memorymex

解决方案


我可以给你一个例子。不幸的是,我无法使用 windows 对其进行测试,但我已经在 UNIX 系统上对其进行了测试。主要思想是一样的。在这种情况下,它是从外部二进制文件到 Matlab mex 函数的共享内存。

外部二进制是:

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

using namespace boost::interprocess;

const std::string payload("SHARED MEMORY CONTENT");

int main(void) {
  shared_memory_object shm(open_or_create, "memory4mat" ,read_write);
  shm.truncate(payload.size());
  mapped_region mem(shm, read_write);
  std::memcpy(mem.get_address(), payload.c_str(), mem.get_size());

  do {
    std::cout << '\n' << "Press a key to continue...";
  } while (std::cin.get() != '\n');

  shared_memory_object::remove("memory4mat");

  return 0;
}

而 mex 函数是:

#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstdlib>
#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;

class MexFunction : public matlab::mex::Function {
public:
    void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {
      matlab::data::ArrayFactory factory;

      shared_memory_object shm(open_only, "memory4mat", read_only);
      mapped_region mem(shm, read_only);

      std::string payload(static_cast<const char *>(mem.get_address()), mem.get_size());
      outputs[0] = factory.createCharArray(payload);
      outputs[1] = factory.createScalar<int16_t>(mem.get_size());
    }
};

它使用Matlab 的 C++ 接口和数据 API。要编译这两个示例,您需要添加 boost 包含目录作为编译器选项(共享内存是 boost 中唯一的标头功能)。

外部二进制文件创建一个包含字符串的共享内存"SHARED MEMORY CONTENT",并等待用户输入以删除共享内存对象。

mex 文件如果存在则打开共享内存(如果共享内存不存在,则会在 Matlab 中报告并处理错误,这是我更喜欢 C++ api 的原因之一)并将其内容复制到 Matlab char 数组中。该函数返回两个值,第一个是共享内存的内容,第二个是共享内存的长度(mapper使用了所有内存,用truncate设置)。

这个简单的例子只使用了基本功能,应该可以在 Unix 和 Windows 系统上运行,但是我不能在 win 上测试。

一个更完整的例子

让我们尝试一个更完整的关于共享内存和 Matlab Mex 文件的示例。让我们编写一个非常简单的外部二进制文件,它允许我们创建/删除/读取/写入共享内存。为了简单起见,这个二进制文件有很多硬编码的东西,例如内存文件的名称 ( "shmem"):

// File: share_server.cpp
// g++ share_server.cpp -o share_server
#include <cstring>
#include <string>
#include <cstdlib>
#include <iostream>
#include <boost/interprocess/shared_memory_object.hpp>
#include <boost/interprocess/mapped_region.hpp>

using namespace boost::interprocess;

static const std::size_t size = 20;
static const std::size_t wsize = 15;
static const char name[6] = "shmem";
static const char input[wsize] = "write in share";
char output[size];

inline void printHelp() {
  std::cout << "Options:" << std::endl;
  std::cout << "  n) open a new 'shmem' memory" << std::endl;
  std::cout << "  d) delete a 'shmem' memory" << std::endl;
  std::cout << "  r) read from 'shmem' memory" << std::endl;
  std::cout << "  w) write to 'shmem' memory" << std::endl;
  std::cout << "  x) Exit" << std::endl;
}

inline void cmd_createShare() {
  try {
    shared_memory_object sm(create_only, name, read_write);
    sm.truncate(size);
    std::cout << "Shared object created" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Create Error :: " << e.what() << std::endl; 
  }
}

inline void cmd_deleteShare() {
  try {
    shared_memory_object::remove(name);
    std::cout << "Shared object deletetd" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Delete Error:: " << e.what() << std::endl; 
  }
}

inline void cmd_readShare() {
  try {
    shared_memory_object sm(open_only,  name, read_only);
    mapped_region sh_mem(sm, read_only);
    std::string ret(static_cast<const char *>(sh_mem.get_address()), sh_mem.get_size());
    std::cout << ret << std::endl;
  } catch(std::exception & e) {
    std::cout << "Read Error:: " << e.what() << std::endl; 
  }
}

inline void cmd_writeShare() {
  try {
    shared_memory_object sm(open_only,  name, read_write);
    mapped_region sh_mem(sm, read_write);
    std::memcpy(sh_mem.get_address(), input, wsize);
    std::cout << "Write completed" << std::endl;
  } catch(std::exception & e) {
    std::cout << "Read Error:: " << e.what() << std::endl; 
  }
}

我们可以编写 3 个 mex 文件(使用 C++ api)以便与共享内存进行交互。第一个,最简单,将共享内存的内容作为字符串读取并返回到 Matlab 工作区。Matlab 语法中的接口类似于:

function [value, read_size] = read_share(share_name)
  ...
end

C++ 实现如下:

// File: read_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t read_shared_memory(const std::string & name, std::string & ret_value) {
    try {
      shared_memory_object sm(open_only,  name.c_str(), read_only);
      mapped_region sh_mem(sm, read_only);
      ret_value += std::string(static_cast<const char *>(sh_mem.get_address()), sh_mem.get_size());
      return ret_value.size();
    } catch(std::exception & e) {
      throwError(std::string("Reading error: ") + std::string(e.what()));
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 1)
      throwError("Input must be of size 1");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (outputs.size() > 2)
      throwError("Too many outputs (required 1)");
  }

public:
  MexFunction() { 
    engine = getEngine();
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);
    const CharArray name_array = std::move(inputs[0]);
    std::string name = name_array.toAscii();
    std::string ret_string("");
    uint64_t ret_size = read_shared_memory(name, ret_string);
    outputs[0] = factory.createScalar(ret_string);
    outputs[1] = factory.createScalar<uint64_t>(ret_size);
  }  
};

第二个mex文件是写操作。它需要两个输入:共享内存的名称和要写入内存的字符串。mex 检查共享内存的最大大小并存储不超过可用空间。该函数返回写入的字节写入函数的接口类似于:

function written_size = write_share(share_name, string)
  ...
end

实施是:

// File: write_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <algorithm>
#include <cstring>
#include <cstdlib>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t write_shared_memory(const std::string & name, const std::string & value) {
    try {
      shared_memory_object sm(open_only,  name.c_str(), read_write);
      mapped_region sh_mem(sm, read_write);
      uint64_t size = std::min(value.size(), sh_mem.get_size());
      std::memcpy(sh_mem.get_address(), value.c_str(), size);
      return size;
    } catch(std::exception & e) {
      throwError(std::string("Reading error: ") + std::string(e.what()));
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 2)
      throwError("Input must be of size 2");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (inputs[1].getType() != ArrayType::CHAR)
      throwError("Second element must be a matlab char array to save");
    if (outputs.size() > 1)
      throwError("Too many outputs (required 1)");
  }

public:
  MexFunction() { 
    engine = getEngine();
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);
    const CharArray name_array = std::move(inputs[0]);
    std::string name = name_array.toAscii();
    const CharArray value_array = std::move(inputs[1]);
    std::string value = value_array.toAscii();
    uint64_t written = write_shared_memory(name, value);
    outputs[0] = factory.createScalar<uint64_t>(written);
  }  
};

最后一个 mex 是最复杂的,它处理共享内存的创建和删除。当从 Matlab 中卸载 mex 时,您会注意到存在一个处理共享内存删除的析构函数。"create"该接口采用or形式的命令,"delete"带有共享名称和要创建的共享内存大小的字符串(它必须是无符号 int - uint16(...))。该函数返回共享内存的大小(它应该等于大小):

function size_shmem = menage_mex(command, share_name, uint16(size))
  ...
end

实施如下:

// File: menage_share.cpp
#include "mex.hpp"
#include "mexAdapter.hpp"
#include "MatlabDataArray.hpp"
#include <string>
#include <cstring>
#include <cstdlib>
#include <vector>
#include <exception>

#include "boost/interprocess/shared_memory_object.hpp"
#include "boost/interprocess/mapped_region.hpp"

using namespace boost::interprocess;
using namespace matlab::data;

class MexFunction : public matlab::mex::Function {
private:
  std::shared_ptr<matlab::engine::MATLABEngine> engine;
  ArrayFactory factory;
  std::vector<std::string> pool; 

  void throwError(std::string errorMessage) {
    engine->feval(matlab::engine::convertUTF8StringToUTF16String("error"),
            0, std::vector<Array>({ factory.createScalar(errorMessage) }));
  }

  uint64_t run_command(const std::string & cmd, const std::string & name, uint64_t size) {
    if (cmd == "create")
      return create_shared_memory(name, size);       
    if (cmd == "delete")
      return delete_shared_memory(name, size);

    throwError("The command is unknown");
    return 0;
  }

  uint64_t create_shared_memory(const std::string & name, uint64_t size) {
    bool in_pool = false;
    for (const auto & el : pool) {
      if (el == name) {
        in_pool = true;
        break;
      }
    }
    if (in_pool) {
      try {
        shared_memory_object sm(open_only, name.c_str(), read_only);
        mapped_region sm_reg(sm, read_only);
        if (sm_reg.get_size() != size)
          throwError("Memory already exist and it is of different size");
        return 0;
      } catch (std::exception & e) {
        throwError(std::string("Cannot open existing shared memory (maybe already open?) :: ") + std::string(e.what()));
      }
    } else {
      try {
        shared_memory_object sm(create_only, name.c_str(), read_write);
        sm.truncate(size);
        pool.push_back(name);
        return size;
      } catch (std::exception & e) {
        throwError(std::string("Cannot create shared memory [" + name + "] (maybe already open?) :: ") + std::string(e.what()));
      }
    }
    return 0;
  }

  uint64_t delete_shared_memory(const std::string & name, uint64_t size) {
    std::size_t in_pool = 0; 
    for (const auto & el : pool) {
      if (el == name)
        break;
      in_pool++;
    }
    if (in_pool < pool.size()) {
      shared_memory_object::remove(name.c_str());
      pool.erase(pool.begin() + in_pool);
    } else {
      throwError("Shared memory [" + name + "] is not handled by this mex");
    }
    return 0;
  }

  void checkArguments(matlab::mex::ArgumentList inputs, matlab::mex::ArgumentList outputs) {
    if (inputs.size() != 3)
      throwError("Input must be of size 3");
    if (inputs[0].getType() != ArrayType::CHAR)
      throwError("First element must be a matlab char array");
    if (inputs[1].getType() != ArrayType::CHAR)
      throwError("Second element must be amatlab char array");
    if (inputs[2].getType() != ArrayType::UINT64)
      throwError("Third element must be a single uint64 integer");
    if (outputs.size() > 1)
      throwError("Too many outputs (required 1)");
  }

  void inputArguments(std::string & cmd, std::string & name, uint64_t & size, matlab::mex::ArgumentList inputs) {
    const CharArray cmd_array = std::move(inputs[0]);
    const CharArray name_array = std::move(inputs[1]);
    const TypedArray<uint64_t> size_array = std::move(inputs[2]);
    cmd = cmd_array.toAscii();
    name = name_array.toAscii();
    size = size_array[0];
  }

public:
  MexFunction() { 
    pool.clear(); 
    engine = getEngine();
  }

  ~MexFunction() {
    for (const auto & el : pool) {
      shared_memory_object::remove(el.c_str());
    }
  }

  void operator()(matlab::mex::ArgumentList outputs, matlab::mex::ArgumentList inputs) {  
    checkArguments(inputs, outputs);

    std::string cmd, name;
    uint64_t size;
    inputArguments(cmd, name, size, inputs);
    uint64_t ret = run_command(cmd, name, size);
    outputs[0] = factory.createScalar<uint64_t>(ret);
  }  
};

要编译 mex,您可以使用以下脚本:

MEX_OPT = ['-I', '/path/to/boost'];

MEX_SRC = { ...
  'menage_share.cpp', ...
  'read_share.cpp', ...
  'write_share.cpp' ...
};

for i = 1:length(MEX_SRC)
  mex(MEX_OPT, MEX_SRC{i});
end

!g++ share_server.cpp -o share_server

您可以按如下方式测试它们:

(MATLAB)                                        | (TERMINAL)
>> menage_share('create', 'shmem', uint64(20))  | 
<< 20                                           |
>> write_share('shmem', 'Hello there')          | $ ./share_server
<< 11                                           |   ( ... help message ... )
                                                | << r
                                                | >> Hello there 

推荐阅读