如何将文件放入 boost::interprocess::managed_shared_memory 中?

2023-12-08

如何将任意名称和任意大小的文件放入boost::interprocess::managed_shared_memory?

注意,我的意思不是boost::interprocess::managed_mapped_file or boost::interprocess::file_mapping.

I chose managed_shared_memory因为其他选项需要固定的文件名 需要指定,但我需要传输具有不同名称的文件。

我需要使用 boost,而不是 Win32 API。

我在网上翻遍了海量的资料,却没有 找到任何合适的东西。

因此,我请求你的帮助。我将非常感谢你。


UPDATE

最后添加了奖励版本。现在这个答案提供了代码的三个完整版本:

  1. Using managed_shared_memory按照要求
  2. Using message_queue作为一种更自然的上传/传输方法
  3. 使用 TCP 套接字 (Asio) 来演示其灵活性

所有这些都仅使用Boost

共享内存管理段包含任意对象。所以你定义一个像这样的对象

 struct MyFile {
     std::string _filename;
     std::vector<char> _contents;
 };

并将其存储在那里。但是,等等,不要那么快,因为这些只能安全地存储在进程间分配器,所以添加一些魔法酱(又名很多有趣的 typedef 来声明分配器,以及一些构造函数):

namespace Shared {
    using Mem = bip::managed_shared_memory;
    using Mgr = Mem::segment_manager;

    template <typename T>
    using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;

    template <typename T> using Vector = bc::vector<T, Alloc<T>>;
    using String =
        bc::basic_string<char, std::char_traits<char>, Alloc<char>>;

    struct MyFile {
        using allocator_type = Alloc<char>;

        template <typename It>
        explicit MyFile(std::string_view name, It b, It e, allocator_type alloc)

        String _filename;
        Vector<char> _contents;
    };
}

现在您可以存储文件,例如:

Shared::Mem shm(bip::open_or_create, "shared_mem", 10ull << 30);

std::ifstream ifs("file_name.txt", std::ios::binary);
std::istreambuf_iterator<char> data_begin{ifs}, data_end{};

auto loaded = shm.find_or_construct<Shared::MyFile>("file1")(
        file.native(), data_begin, data_end,
         shm.get_segment_manager());

请注意,共享内存实际上不会立即占用 30GiB,即使 就是这样10ull << 30指定。在大多数操作系统上,这将是 稀疏分配,并且只有包含数据的页面才会被提交。

改善

您可能想知道什么是scoped_allocator_adaptor是为了。我们好像没用过吧?

嗯,我们的想法是not use find_or_construct直接每个文件,但要 存储一个Vector<MyFile这样您就可以利用 BIP 分配器的全部功能。

可以调用以下完整demo

  • 带有文件名参数,这些参数都将被加载(如果它们存在于 常规文件)
  • 不带参数,它将列出以前加载的文件

住在科里鲁

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/managed_mapped_file.hpp> // for COLIRU
#include <boost/interprocess/containers/vector.hpp>
#include <boost/interprocess/containers/string.hpp>
#include <boost/container/scoped_allocator.hpp>
#include <fstream>
#include <filesystem>
#include <iostream>
#include <iomanip>

namespace bip = boost::interprocess;
namespace bc = boost::container;
namespace fs = std::filesystem;

namespace Shared {
#ifdef COLIRU
    using Mem = bip::managed_mapped_file; // managed_shared_memory not allows
#else
    using Mem = bip::managed_shared_memory;
#endif
    using Mgr = Mem::segment_manager;

    template <typename T>
    using Alloc = bc::scoped_allocator_adaptor<bip::allocator<T, Mgr>>;

    template <typename T> using Vector = bc::vector<T, Alloc<T>>;
    using String = bc::basic_string<char, std::char_traits<char>, Alloc<char>>;

    struct MyFile {
        using allocator_type = Alloc<char>;

        MyFile(MyFile&&) = default;
        MyFile(MyFile const& rhs, allocator_type alloc)
            : _filename(rhs._filename.begin(), rhs._filename.end(), alloc),
              _contents(rhs._contents.begin(), rhs._contents.end(), alloc) {}

        MyFile& operator=(MyFile const& rhs) {
            _filename.assign(rhs._filename.begin(), rhs._filename.end());
            _contents.assign(rhs._contents.begin(), rhs._contents.end());
            return *this;
        }

        template <typename It>
        explicit MyFile(std::string_view name, It b, It e, allocator_type alloc)
            : _filename(name.data(), name.size(), alloc),
              _contents(b, e, alloc) {}

        String _filename;
        Vector<char> _contents;

        friend std::ostream& operator<<(std::ostream& os, MyFile const& mf) {
            return os << "Name: " << std::quoted(mf._filename.c_str())
                      << " content size: " << mf._contents.size();
        }
    };
} // namespace Shared

int main(int argc, char** argv) {
    Shared::Mem shm(bip::open_or_create, "shared_mem", 512ull << 10);

    using FileList = Shared::Vector<Shared::MyFile>;
    auto& shared_files =
        *shm.find_or_construct<FileList>("FileList")(shm.get_segment_manager());

    if (1==argc) {
        std::cout << "Displaying previously loaded files: \n";
        for (auto& entry : shared_files)
            std::cout << entry << std::endl;
    } else {
        std::cout << "Loading files: \n";
        for (auto file : std::vector<fs::path>{argv + 1, argv + argc}) {
            if (is_regular_file(file)) {
                try {
                    std::ifstream ifs(file, std::ios::binary);
                    std::istreambuf_iterator<char> data_begin{ifs}, data_end{};

                    auto& loaded = shared_files.emplace_back(
                        file.native(), data_begin, data_end);

                    std::cout << loaded << std::endl;
                } catch (std::system_error const& se) {
                    std::cerr << "Error: " << se.code().message() << std::endl;
                } catch (std::exception const& se) {
                    std::cerr << "Other: " << se.what() << std::endl;
                }
            }
        }
    }
}

当运行时

g++ -std=c++17 -O2 -Wall -pedantic -pthread main.cpp -lrt -DCOLIRU
./a.out main.cpp a.out
./a.out

Prints

Loading files: 
Name: "main.cpp" content size: 3239
Name: "a.out" content size: 175176
Displaying previously loaded files: 
Name: "main.cpp" content size: 3239
Name: "a.out" content size: 175176

BONUS

针对评论,我认为值得进行实际比较

消息队列版本

为了进行比较,这里有一个消息队列实现

住在科里鲁

#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/endian/arithmetic.hpp>
#include <fstream>
#include <filesystem>
#include <iostream>
#include <iomanip>

namespace bip = boost::interprocess;
namespace fs = std::filesystem;
using bip::message_queue;
static constexpr auto MAX_FILENAME_LENGH = 512; // 512 bytes max filename length
static constexpr auto MAX_CONTENT_SIZE = 512ull << 10; // 512 KiB max payload size

struct Message {
    std::vector<char> _buffer;

    using Uint32 = boost::endian::big_uint32_t;
    struct header_t {
        Uint32 filename_length;
        Uint32 content_size;
    };
    static_assert(std::is_standard_layout_v<header_t> and
                  std::is_trivial_v<header_t>);

    Message() = default;

    Message(fs::path file) {
        std::string const name = file.native();
        std::ifstream ifs(file, std::ios::binary);
        std::istreambuf_iterator<char> data_begin{ifs}, data_end{};

        _buffer.resize(header_len + name.length());
        std::copy(begin(name), end(name), _buffer.data() + header_len);
        _buffer.insert(_buffer.end(), data_begin, data_end);
        header().filename_length = name.length();
        header().content_size    = size() - header_len - name.length();
    }

    Message(char const* buf, size_t size) 
        : _buffer(buf, buf+size) {}

    static constexpr auto header_len = sizeof(header_t);
    static constexpr auto max_size =
        header_len + MAX_FILENAME_LENGH + MAX_CONTENT_SIZE;

    char const* data() const { return _buffer.data(); } 
    size_t size() const      { return _buffer.size(); } 

    header_t& header() {
        assert(_buffer.size() >= header_len);
        return *reinterpret_cast<header_t*>(_buffer.data());
    }

    header_t const& header() const {
        assert(_buffer.size() >= header_len);
        return *reinterpret_cast<header_t const*>(_buffer.data());
    }

    std::string_view filename() const { 
        assert(_buffer.size() >= header_len + header().filename_length);
        return { _buffer.data() + header_len, header().filename_length };
    }

    std::string_view contents() const {
        assert(_buffer.size() >=
                header_len + header().filename_length + header().content_size);

        return {_buffer.data() + header_len + header().filename_length,
            header().content_size};
    }

    friend std::ostream& operator<<(std::ostream& os, Message const& mf) {
        return os << "Name: " << std::quoted(mf.filename())
                  << " content size: " << mf.contents().size();
    }
};

int main(int argc, char** argv) {
    message_queue mq(bip::open_or_create, "file_transport", 10, Message::max_size);

    if (1==argc) {
        std::cout << "Receiving uploaded files: \n";
        char rawbuf [Message::max_size];
        while (true) {
            size_t n;
            unsigned prio;
            mq.receive(rawbuf, sizeof(rawbuf), n, prio);

            Message decoded(rawbuf, n);
            std::cout << "Received: " << decoded << std::endl;
        }
    } else {
        std::cout << "Loading files: \n";
        for (auto file : std::vector<fs::path>{argv + 1, argv + argc}) {
            if (is_regular_file(file)) {
                try {
                    Message encoded(file);
                    std::cout << "Sending: " << encoded << std::endl;

                    mq.send(encoded.data(), encoded.size(), 0);
                } catch (std::system_error const& se) {
                    std::cerr << "Error: " << se.code().message() << std::endl;
                } catch (std::exception const& se) {
                    std::cerr << "Other: " << se.what() << std::endl;
                }
            }
        }
    }
}

A demo:

enter image description here

请注意,此方法存在文件大小限制,因为消息具有最大长度

TCP 套接字版本

这是一个 TCP 套接字实现。

住在科里鲁

#include <boost/asio.hpp>
#include <boost/endian/arithmetic.hpp>
#include <vector>
#include <fstream>
#include <filesystem>
#include <iostream>
#include <iomanip>

namespace fs = std::filesystem;
using boost::asio::ip::tcp;
using boost::system::error_code;
static constexpr auto MAX_FILENAME_LENGH = 512; // 512 bytes max filename length
static constexpr auto MAX_CONTENT_SIZE = 512ull << 10; // 512 KiB max payload size

struct Message {
    std::vector<char> _buffer;

    using Uint32 = boost::endian::big_uint32_t;
    struct header_t {
        Uint32 filename_length;
        Uint32 content_size;
    };
    static_assert(std::is_standard_layout_v<header_t> and
                  std::is_trivial_v<header_t>);

    Message() = default;

    Message(fs::path file) {
        std::string const name = file.native();
        std::ifstream ifs(file, std::ios::binary);
        std::istreambuf_iterator<char> data_begin{ifs}, data_end{};

        _buffer.resize(header_len + name.length());
        std::copy(begin(name), end(name), _buffer.data() + header_len);
        _buffer.insert(_buffer.end(), data_begin, data_end);
        header().filename_length = name.length();
        header().content_size    = actual_size() - header_len - name.length();
    }

    Message(char const* buf, size_t size) 
        : _buffer(buf, buf+size) {}

    static constexpr auto header_len = sizeof(header_t);
    static constexpr auto max_size =
        header_len + MAX_FILENAME_LENGH + MAX_CONTENT_SIZE;

    char const* data() const { return _buffer.data(); }
    size_t actual_size() const { return _buffer.size(); }
    size_t decoded_size() const {
        return header().filename_length + header().content_size;
    }
    bool is_complete() const {
        return actual_size() >= header_len && actual_size() >= decoded_size();
    }

    header_t& header() {
        assert(actual_size() >= header_len);
        return *reinterpret_cast<header_t*>(_buffer.data());
    }

    header_t const& header() const {
        assert(actual_size() >= header_len);
        return *reinterpret_cast<header_t const*>(_buffer.data());
    }

    std::string_view filename() const { 
        assert(actual_size() >= header_len + header().filename_length);
        return std::string_view(_buffer.data() + header_len,
                                header().filename_length);
    }

    std::string_view contents() const {
        assert(actual_size() >= decoded_size());

        return std::string_view(_buffer.data() + header_len +
                                    header().filename_length,
                                header().content_size);
    }

    friend std::ostream& operator<<(std::ostream& os, Message const& mf) {
        return os << "Name: " << std::quoted(mf.filename())
                  << " content size: " << mf.contents().size();
    }
};

int main(int argc, char** argv) {
    boost::asio::io_context ctx;
    u_int16_t port = 8989;

    if (1==argc) {
        std::cout << "Receiving uploaded files: " << std::endl;
        tcp::acceptor acc(ctx, tcp::endpoint{{}, port});

        while (true) {
            auto s = acc.accept();
            std::cout << "Connection accepted from " << s.remote_endpoint() << std::endl;

            Message msg;
            auto buf = boost::asio::dynamic_buffer(msg._buffer);
            error_code ec;
            while (auto n = read(s, buf, ec)) {
                std::cout << "(read " << n << " bytes, " << ec.message() << ")" << std::endl;

                while (msg.is_complete()) {
                    std::cout << "Received: " << msg << std::endl;
                    buf.consume(msg.decoded_size() + Message::header_len);
                }
            }
            std::cout << "Connection closed" << std::endl;
        }
    } else {
        std::cout << "Loading files: " << std::endl;
        tcp::socket s(ctx);
        s.connect(tcp::endpoint{{}, port});

        for (auto file : std::vector<fs::path>{argv + 1, argv + argc}) {
            if (is_regular_file(file)) {
                try {
                    Message encoded(file);
                    std::cout << "Sending: " << encoded << std::endl;

                    write(s, boost::asio::buffer(encoded._buffer));
               } catch (std::system_error const& se) {
                    std::cerr << "Error: " << se.code().message() << std::endl;
                } catch (std::exception const& se) {
                    std::cerr << "Other: " << se.what() << std::endl;
                }
            }
        }
    }
}

Demo:

enter image description here

请注意,这如何轻松地扩展到更大的文件、单个连接中的多个文件,甚至如果需要,可以同时扩展到多个连接。它还不进行双缓冲,这提高了性能。 这就是为什么这种方法比任何其他方法更常见的原因。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何将文件放入 boost::interprocess::managed_shared_memory 中? 的相关文章

  • 如何转发声明要在 unique_ptr 的标准容器中使用的类

    在智能指针的标准容器中使用它时 是否可以避免完整的类定义可见 例如 我无法编译以下内容 include
  • C语言实现延时函数

    我想使用空循环实现延迟函数 但是完成一次循环所需的时间取决于编译器和机器 我希望我的程序自行确定时间并将程序延迟指定的时间 谁能给我任何想法如何做到这一点 注意 有一个名为delay 的函数可以将系统暂停指定的毫秒 是否可以在不使用此功能的
  • 更改图像颜色与透明背景

    我需要使用 c System Drawings 将透明背景上带有绿色圆圈的图像加载到位图图像中 这是最简单的部分 但是 我需要在将其添加到更大的图像之前更改圆圈的颜色 而不影响周围的透明度 就我而言 我需要将圆圈颜色更改为黄色并将其添加为太
  • 提取单花括号内的值

    我想要一个收藏 value 一个字符串使用正则表达式 例如 lorem ipsum field1 lorem ipsum field2 lorem ipsum field1 lorem ipsum field2 field3 我会得到 fi
  • .NET 中的 Class.forName() 等效项?

    动态获取对象类型然后创建它的新实例的 C 方法是什么 例如 如何在 C 中实现以下 Java 代码的结果 MyClass x MyClass Class forName classes MyChildClass newInstance Lo
  • 为什么 fgets 接受 int 而不是 size_t?

    功能如strcpy malloc strlen 和其他各种接受他们的参数或返回值作为size t代替int or an unsigned int出于显而易见的原因 一些文件功能 例如fread and fwrite use size t以及
  • 值类型如何实现引用类型

    我遇到了一个值类型正在实现 ref 的场景 类型 只是想知道这怎么可能 幕后发生了什么 结构体是值类型 接口是引用 类型但结构可以实现接口而不会出现任何错误 有什么想法吗 提前致谢 实际上 它同时以两种不同的方式进行 首先 任何值类型都可以
  • 使用 LINQ 展平嵌套字典

    所以我有一本形式的字典Dictionary
  • JNA Windows 服务启动类型

    我一直在使用 JNA 并且能够使用下面的代码返回 Windows 服务的状态 即启动或停止 但我不确定如何返回服务的启动类型 我确信 JNA 之外还有其他方法 但如果可能的话我想继续使用 JNA import com sun jna imp
  • 可以通过模板间接访问基类中的私有类型

    我试图在编译时根据类型是否在给定范围内公开可用来选择要使用的类型 最好直接看代码 include
  • 将旧的 Unity 代码升级到 Unity 5

    在触发按钮上播放动画的代码似乎不起作用 我在 Youtube 上看到了一个视频 内容很简单animation Play 它可以在该视频上运行 但我无法让它在我的计算机上运行 我做错了什么还是团结改变了它 请帮助我在网上找不到解决方案 所有
  • 为什么最小的 int -2147483648 的类型为“long”? [复制]

    这个问题在这里已经有答案了 对于一个学校项目 我必须编写 C 函数 printf 的代码 一切进展顺利 但有一个问题我找不到好的答案 所以我来了 printf PRINTF d t d n 2147483648 告诉我 gcc Werror
  • WCF 服务中的缓冲区大小

    我们有一个 WCF 服务 它执行某些存储过程并将结果返回给 silverlight 客户端 某些存储过程最多返回 80K 行 下面给出的是 web config 中服务的设置
  • 我的代码哪里有泄漏?

    下面是我的代码 它打开一个 XML 文件 old xml 过滤无效字符并写入另一个 XML 文件 abc xml 最后 我将再次加载 XML abc xml 当执行以下行时 出现异常 表示 xml 文件被另一个进程使用 xDoc Load
  • 链接到ntdll.lib并调用ntdll.dll内部的函数

    我最近正在对私有 API 进行一些研究 我尝试调用诸如NtOpenFile在 ntdll dll 中LoadLibrary and GetProcAddress在运行时 幸运的是 它成功了 今天早上我在电脑上进行了文件搜索 发现ntdll
  • .NET 或 Windows 同步原语性能规范

    我目前正在写一篇科学文章 我需要非常准确地引用 有人可以向我指出 MSDN MSDN 文章 一些已发表的文章来源或一本书 我可以在其中找到 Windows 或 NET 同步原语的性能比较 我知道这些是按性能降序排列的 互锁 API 关键部分
  • NSubstitute - 测试特定的 linq 表达式

    我在当前正在开发的 MVC 3 应用程序中使用存储库模式 我的存储库界面如下所示 public interface IRepository
  • 为什么我无法通过 lambda 捕获“this”指针?

    考虑以下代码 class A public void foo auto functor this A a this auto functor a The compiler won t accept this instead of a a g
  • 使用 DataGridViewCheckboxCell 真正禁用 DataGridView 中的复选框

    有谁知道如何使用 DataGridViewCheckboxCell 禁用 DataGridView 中的复选框 我可以将其设置为只读 并设置背景颜色 但我无法让复选框本身显示为禁用状态 有什么想法吗 Guess 你必须自己画 http so
  • SMTP 客户端在 C# 应用程序中显示错误“未采取请求的操作”

    我正在尝试使用 hotmail 帐户设置电子邮件发送应用程序 代码如下所示 MailMessage mail new MailMessage from to mail Subject Proba email mail Attachments

随机推荐