Я могу привести пример. К сожалению, я не могу проверить это с Windows, но я проверил это в системе UNIX. Основная идея такая же. В этом случае это разделяемая память из внешнего двоичного файла в mex-функцию Matlab.
Внешний двоичный файл:
#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());
}
};
он использует C ++ интерфейс и API данных для Matlab . Чтобы скомпилировать два примера, вам нужно добавить каталог включения Boost в качестве параметров компилятора (общая память - это функция заголовка только в boost).
Внешний двоичный файл создает общую память , которая содержит строку "SHARED MEMORY CONTENT"
и ожидает ввода от пользователя, чтобы удалить объект общей памяти.
mex-файлы открывают разделяемую память, если она существует (если разделяемая память не существует, об ошибке сообщается и обрабатывается в Matlab, что является одной из причин, почему я предпочитаю C ++ api), и копирую ее содержимое в массив символов Matlab , Функция возвращает два значения: первое - это содержимое разделяемой памяти, второе - длина разделяемой памяти (маппер использует всю память, установленную с помощью усечения).
Этот простой пример использует только основные функции и должен работать в системах 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 файла (используя API C ++) для взаимодействия с разделяемой памятью. Первый, самый простой, считывает содержимое разделяемой памяти в виде строки и возвращает его в рабочую область 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-файл - операция записи. Требуется два ввода: имя разделяемой памяти и строка для записи в память. Мекс проверяет максимальный размер разделяемой памяти и сохраняет не более доступного пространства. Функция возвращает байты, записанные в интерфейсе для функции записи, что-то вроде:
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);
}
};
Последний мекс является наиболее сложным и обрабатывает создание и удаление общей памяти. Вы заметите присутствие деструктора, который обрабатывает удаление общей памяти, когда мекс выгружается из Matlab. Интерфейс принимает команду в виде "create"
или "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