boost find in共享内存方法陷入c++多进程项目中

2024-02-09

我正在使用 boost 的 ipc 库来保存复杂的对象,包括图像,在共享内存中,由多个进程使用。我们称这个对象为MyImage。共享内存是一个循环缓冲区,保存了几个MyImage一次对象。

在我的代码中,有两个(或更多)进程写入共享内存中的一个段,另一个进程从中读取。此流程按预期工作,但是在读取器进程完成或崩溃后,当它尝试再次打开共享内存中的同一对象时,它会卡住find方法,而编写器进程仍然运行良好。

我试图了解哪种竞争条件可能导致此问题,但在我的代码或 boost 的文档中找不到任何解释。

这是我项目中问题的一个简单代码示例:

The Writer过程:

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/circular_buffer.hpp>

using namespace std;
namespace bip = boost::interprocess;

static const char *const PLACE_SHM_NAME = "PlaceInShm";
static const char *const OBJECT_SHM_NAME = "ObjectInShm";
static const char *const PUSH_POP_LOCK = "push_pop_image_lock";
static const int IMAGES_IN_BUFFER = 20;
static const int OBJECT_SIZE_IN_SHM = 91243520;

class MyImage;

typedef bip::managed_shared_memory::segment_manager SegmentManagerType;
typedef bip::allocator<void, SegmentManagerType> MyImageVoidAllocator;
typedef bip::deleter<MyImage, SegmentManagerType> MyImageDeleter;
typedef bip::shared_ptr<MyImage, MyImageVoidAllocator, MyImageDeleter> MyImageSharedPtr;

typedef bip::allocator<MyImageSharedPtr, bip::managed_shared_memory::segment_manager> MyImageShmemAllocator;
typedef boost::circular_buffer<MyImageSharedPtr, MyImageShmemAllocator> MyImageContainer;

MyImageSharedPtr GetMyImage() {
    // some implementation
    return nullptr;
}

int main(int argc, char *argv[]) {

    MyImageContainer *my_image_data_container;
    try {
        bip::named_mutex open_lock{bip::open_or_create, OPEN_SHM_LOCK};
        bip::managed_shared_memory image_segment = bip::managed_shared_memory(bip::open_or_create, PLACE_SHM_NAME, OBJECT_SIZE_IN_SHM);
        my_image_data_container = image_segment.find_or_construct<MyImageContainer>(OBJECT_SHM_NAME)(IMAGES_IN_BUFFER, image_segment.get_segment_manager());
    } catch (boost::interprocess::interprocess_exception &e) {
        exit(1);
    }
    boost::interprocess::named_mutex my_image_mutex_ptr(boost::interprocess::open_or_create, PUSH_POP_LOCK);

    while (true) {
        MyImageSharedPtr img = GetMyImage();
        my_image_mutex_ptr.lock();
        my_image_data_container->push_back(img);
        my_image_mutex_ptr.unlock();
        usleep(1000);
    }
}    

The Reader过程:

int main(int argc, char *argv[]) {

    MyImageContainer *my_image_data_container;
    try {
        bip::named_mutex open_lock{bip::open_only, OPEN_SHM_LOCK};
        bip::scoped_lock<bip::named_mutex> lock(open_lock, bip::try_to_lock);
        bip::managed_shared_memory image_segment = bip::managed_shared_memory(bip::open_only, PLACE_SHM_NAME);
        my_image_data_container = image_segment.find<MyImageContainer>(OBJECT_SHM_NAME).first;
    } catch (boost::interprocess::interprocess_exception &e) {
        exit(1);
    }
    boost::interprocess::named_mutex my_image_mutex_ptr(boost::interprocess::open_or_create, PUSH_POP_LOCK);

    while (true) {
        if (my_image_data_container->size() == 0) {
            continue;
        }
        MyImage *img;
        my_image_mutex_ptr.lock();
        img = &(*my_image_data_container->at(0));
        my_image_data_container->pop_front();
        my_image_mutex_ptr.unlock();
        // do stuff with img
        usleep(1000);
    }
}

重现该错误的流程:

  1. 运行两个进程Writer code.
  2. 运行其中一个进程Reader code.
  3. 杀死Reader过程。
  4. run the Reader再次处理。

第二次运行时,进程卡在队列中image_segment.find<MyImageContainer>(OBJECT_SHM_NAME).first;Writer流程都很好。

值得一提的是,每个Writer进程有唯一的id,并且只写入共享内存中的缓冲区int(IMAGES_IN_BUFFER / NUMBER_OF_WRITERS)图像从索引开始作为他的 id。 例如,我有两个Writerid 为 0 和 id 1 的 s,IMAGES_IN_BUFFER=20, then Writer 0将写入索引 0-9 和Writer 1至 10-19。

我的一些调试过程:

  • 我尝试在单独的线程中打开共享内存,使用future对象,并设置几秒钟的超时。但整个过程还是卡住了。
  • 当我在卡住后终止该进程并重新运行它时,它永远不会再次成功,除非我从共享内存中删除该对象并重新运行所有进程,包括Writers.
  • 通常与一个人一起跑步时Writer我无法重现该错误,但我不能确定。
  • 它不一致,这意味着我无法判断它何时会被卡住,何时不会被卡住。
  • 也许共享内存中的对象以某种方式损坏了,而Reader进程崩溃,然后重新打开它时,它失败了。在这种情况下,我预计 boost 将引发异常而不是挂起。
  • 当进程正常退出(退出代码为 0)时,也可能会发生这种情况。

等待听到一些关于可能导致流程卡住的原因的意见。 提前致谢!


我发现您的代码有很多问题。除此之外,还有一个已知的限制。那么让我们从那开始吧

进程间互斥体的鲁棒性

首先,该库的一个众所周知的问题是没有强大的进程间互斥体(可移植):

  • 如何获得废弃的 boost::interprocess::interprocess_mutex 的所有权? https://stackoverflow.com/questions/1179685/how-do-i-take-ownership-of-an-abandoned-boostinterprocessinterprocess-mutex
  • 增强进程间互斥并检查放弃 https://stackoverflow.com/questions/15772768/boost-interprocess-mutexes-and-checking-for-abandonment etc.
  • 但也看到https://www.boost.org/doc/libs/1_76_0/boost/interprocess/detail/robust_emulation.hpp https://www.boost.org/doc/libs/1_76_0/boost/interprocess/detail/robust_emulation.hpp我认为在某些平台/文件系统上有使用文件锁的替代方案。

因此,您能做的最好的事情就是进行定时等待,并有一个“强制清除”选项,当您知道这样做是安全的时,您可以手动启用该选项。

代码问题和审查

也就是说,代码存在一些问题,您可以改进一些东西以减少脆弱性。

  1. 你提到了崩溃。这是不言而喻的:崩溃会破坏不变量,避免它们。

  2. 这可能包括拥有适当的中断信号处理程序以确保正确关闭。

  3. 您永远不会将打开的锁锁定在写入器路径中。这是应该解决的明显问题。

  4. 在阅读器路径中,您发出try_lock但似乎从未验证过它是否成功。

  5. 在所示的代码中,您正在使用my_image_data_container共享内存段被破坏后。根据定义,这将永远是未定义的行为 https://en.wikipedia.org/wiki/Undefined_behavior

  6. 您没有将启用 RAII 的锁防护用于推入/弹出互斥体(my_image_mutex_ptr)。这意味着它不是异常安全的,并且会再次导致锁卡在异常上。

  7. 一般来说,您似乎将可锁定原语与锁混淆了。我建议重命名对象(open_lock -> open_mutex, my_image_mutex_ptr(?!)->modify_mutex)以避免这种混乱。

  8. 我可能会建议使用相同的互斥体来打开和修改(毕竟,在修改期间实际上不允许创建段,不是吗?)。或者,考虑在共享段内使用未命名的进程间互斥体来消除不必要的 SHM 命名空间污染。即使在删除共享内存段本身之后,也少了一个可能被卡住的锁!)。

  9. The ->empty()检查是数据竞争:

    • 它不会锁定修改互斥锁,因此容器可能会同时写入
    • 它没有持有相同的锁,所以在从empty()返回值不再可靠,因为同时某些内容可能已被修改

    在 C++ 中,数据竞争也会调用未定义的行为 https://en.wikipedia.org/wiki/Undefined_behavior

  10. 这里有一个很大的问题:

    img = &(*container->at(0));
    

    这会取消引用共享指针,仅保留原始指针。然而,下一行pop_front()s 从容器中删除,因此共享指针被删除,可能(很可能,给出所示的代码)破坏图像。

    只是不要丢失引用计数,并使用共享指针。

  11. 许多名称可以改进以提高可读性。你通常会认为“计算机不在乎”,但人类却在乎。所有的微观混乱都混合在一起,这可能解释了本文中发现的 50% 的错误。

  12. 一些未解决的问题(例外情况最好由const&;你应该检查布尔值find<>(), container->at(0)可以拼写为container->front() etc.)

计数器演示

下面是针对上述内容进行审查的代码版本,并使用更现代的 C++ 风格编写。编写器和读取器现在位于一个主程序中(您可以使用任意命令行参数进行切换)。

住在科里鲁 http://coliru.stacked-crooked.com/

#include <boost/interprocess/managed_shared_memory.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <boost/interprocess/sync/named_mutex.hpp>
#include <boost/interprocess/smart_ptr/shared_ptr.hpp>
#include <boost/interprocess/allocators/allocator.hpp>
#include <boost/circular_buffer.hpp>
#include <iostream>
#include <mutex>
#include <thread>

namespace bip = boost::interprocess;
using namespace std::chrono_literals;
constexpr char const* OPEN_SHM_LOCK = "OPEN_SHM_LOCK";

static const char* const SHM_NAME         = "PlaceInShm";
static const char* const OBJ_NAME         = "ObjectInShm";
static const char* const PUSH_POP_LOCK    = "push_pop_image_lock";
static const int         BUF_CAPACITY = 20;
static const int         SHM_SIZE         = 91243520;

using Segment = bip::managed_shared_memory;
using Mgr     = Segment::segment_manager;

class MyImage{};

template <typename T> using Alloc = bip::allocator<T, Mgr>;
using MyDeleter                   = bip::deleter<MyImage, Mgr>;
using SharedImage = bip::shared_ptr<MyImage, Alloc<MyImage>, MyDeleter>;

using Container = boost::circular_buffer<SharedImage, Alloc<SharedImage>>;

SharedImage GetMyImage() {
    // some implementation
    return {};
}

int main(int argc, char**) try {
    bool const isWriter = (argc == 1);
    std::cout << (isWriter? "Writer":"Reader") << std::endl;

    // extract variable part to reduce code duplication
    auto find_container = [isWriter](Segment& smt) {
        if (isWriter)
            return smt.find_or_construct<Container>(OBJ_NAME)(
                BUF_CAPACITY, smt.get_segment_manager());

        auto [container, ok] = smt.find<Container>(OBJ_NAME);
        assert(ok); // TODO proper error handling?

        return container;
    };

    bip::named_mutex open_mutex{bip::open_or_create, OPEN_SHM_LOCK};
    if (std::unique_lock open_lk{open_mutex, std::try_to_lock}) {
        Segment smt(bip::open_or_create, SHM_NAME, SHM_SIZE);
        auto container = find_container(smt);

        open_lk.unlock();

        bip::named_mutex modify_mutex(bip::open_or_create, PUSH_POP_LOCK);

        while (isWriter) {
            SharedImage img = GetMyImage();

            {
                std::unique_lock lk(modify_mutex);
                container->push_back(img);
            }
            std::cout << "Pushed" << std::endl;
            std::this_thread::sleep_for(1s);
        }

        while (not isWriter) {
            SharedImage img;

            if (std::unique_lock lk(modify_mutex); !container->empty()) {
                img = std::move(container->front());
                container->pop_front();
            } else {
                continue;
            }

            // if (img)
            {
                // do stuff with img
                std::cout << "Popped" << std::endl;
            }

            std::this_thread::sleep_for(1s);
        }
    } else {
        std::cout << "Failed to acquire open lock" << std::endl;
    }
} catch (bip::interprocess_exception const& e) {
    std::cerr << "Error: " << e.what() << std::endl;
    exit(1);
}

在我的系统上运行得很好。我留给读者作为练习:替换修改锁并添加用于关闭的信号处理程序。

录制的演示

这是一个简单的录制演示,演示了它在我的系统上按预期工作,使用不同数量的读取器/写入器:

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

boost find in共享内存方法陷入c++多进程项目中 的相关文章

  • 是否需要销毁运算符删除的形式才能真正销毁对象?

    C 20 添加了破坏形式operator delete区别于std destroying delete t范围 它导致delete表达式在调用之前不再销毁对象operator delete 目的是在显式调用对象的析构函数和释放内存之前 允许
  • 是否可以使用 http url 作为 DirectShow .Net 中源过滤器的源位置?

    我正在使用 DirectShow Net 库创建一个过滤器图 该过滤器图通过使用 http 地址和 WM Asf Writer 来流式传输视频 然后 在网页上 我可以使用对象元素在 Windows Media Player 对象中呈现视频源
  • 捕获 .aspx 和 .ascx 页面中的异常

    问题说明了一切 请看以下示例代码 ul li li ul
  • 无法继承形状

    为什么我不能使用继承 a 的类Shapes class http msdn microsoft com en us library ms604615 28v vs 90 29 我需要延长Rectangle具有一些方法的类 但我想以与使用相同
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • 防止控制台应用程序中的内存工作集最小化?

    我想防止控制台应用程序中的内存工作集最小化 在Windows应用程序中 我可以这样做覆盖 SC MINIMIZE 消息 http support microsoft com kb 293215 en us fr 1 但是 如何在控制台应用程
  • Makefile 和 .Mak 文件 + CodeBlocks 和 VStudio

    我对整个 makefile 概念有点陌生 所以我对此有一些疑问 我正在 Linux 中使用 CodeBlocks 创建一个项目 我使用一个名为 cbp2mak 的工具从 CodeBlocks 项目创建一个 make 文件 如果有人知道更好的
  • Libev,如何将参数传递给相关回调

    我陷入了 libev 中争论的境地 通常 libev 在类似的函数中接收包 接收回调 没关系 但是实际操作中 我们需要派遣一个亲戚 写回调 根据收到的包裹处理具体工作 例如 S RECV MSG pstRecvMsg S RECV MSG
  • 来自嵌入图像的 BitmapSource

    我的目标是在 WPF 窗口上重写 OnRender 方法中绘制图像 someImage png 它是嵌入资源 protected override void OnRender System Windows Media DrawingCont
  • LinkLabel 无下划线 - Compact Framework

    我正在使用 Microsoft Compact Framework 开发 Windows CE 应用程序 我必须使用 LinkLabel 它必须是白色且没有下划线 因此 在设计器中 我将字体颜色修改为白色 并在字体对话框中取消选中 下划线
  • wordexp 失败时我们需要调用 wordfree 吗?

    wordexp 失败时我们需要调用 wordfree 吗 在某些情况下 调用 wordfree 似乎会出现段错误 例如 当 wordfree 返回字符串为 foo bar 的错误代码时 这在手册页中并不清楚 我已经看到在某些错误情况下使用了
  • 如何在 Javascript 中连接 C# ActiveX 事件处理程序

    我尝试使用几个代码片段将 ActiveX 对象与 Javascript 事件处理程序挂钩 我无法确定为什么事件处理程序没有被调用 带有项目的 Github 存储库 https github com JesseKPhillips Csharp
  • 如何防止 Blazor NavLink 组件的默认导航

    从 Blazor 3 1 Preview 2 开始 应该可以防止默认导航行为 https devblogs microsoft com aspnet asp net core updates in net core 3 1 preview
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • 在哪里可以找到 Microsoft.Build.Utilities.v3.5

    如何获取 Microsoft Build Utilities v3 5 我正在使用 StyleCop 4 7 Stylecop dll 中的 StyleCop msbuild 任务似乎依赖于 Microsoft Build Utilitie
  • 调用 .ToArray() 时出现 ArgumentException

    我有一个经常被清除的列表 代码完全是这样的 VisitorAgent toPersist List
  • 如何高效计算连续数的数字积?

    我正在尝试计算数字序列中每个数字的数字乘积 例如 21 22 23 98 99 将会 2 4 6 72 81 为了降低复杂性 我只会考虑 连续的数字 http simple wikipedia org wiki Consecutive in
  • 如何获取带有某个属性注释的所有属性?

    我刚刚从 Roslyn 开始 我想找到所有用属性名称 OneToOne 注释的属性 我启动了 SyntaxVisualizer 并能够获取对该节点的引用 但我想知道是否有更简单的方法来实现此目的 这就是我所拥有的 var prop docu
  • 如何在 C# 中获取 CMD/控制台编码

    我需要指定正确的代码页来使用 zip 库打包文件 正如我所见 我需要指定控制台编码 在我的例子中为 866 C Users User gt mode Status for device CON Lines 300 Columns 130 K
  • 如何为有时异步的操作创建和实现接口

    假设我有数百个类 它们使用 计算 方法实现公共接口 一些类将执行异步 例如读取文件 而实现相同接口的其他类将执行同步代码 例如将两个数字相加 为了维护和性能 对此进行编码的好方法是什么 到目前为止我读到的帖子总是建议将异步 等待方法冒泡给调

随机推荐

  • os Catalina 和 Laravel Homestead MySql 错误:ERROR 2007 (HY000):协议不匹配;服务器版本 = 11,客户端版本 = 10

    我正在尝试连接到MySQLVagrant 版本中的服务器Laravel Homestead 运行MySQL 5 7 我似乎从来没有遇到过任何问题 但在装有 Catalina 的新笔记本电脑上 尝试使用推荐设置进行连接时出现以下错误 mysq
  • 从 MailItem (Microsoft.Office.Interop.Outlook) 获取唯一 ID?

    我正在创建一个应用程序来读取 Outlook 中收到的电子邮件 读取的过程大概是这样的 using Outlook Microsoft Office Interop Outlook var app new Outlook Applicati
  • 类似于 Bitbucket 的软件,我可以自行托管

    是否有类似于 Bitbucket 的系统可供我自行托管 我试图在网上四处看看是否有什么东西 但我似乎找不到任何东西 我们现在正在使用Redmine 但Redmine不支持每个项目多个存储库 我希望能够实现的 Bitbucket 功能包括分叉
  • 公共/受保护的类属性不会覆盖父类的私有属性吗?

    在父类上声明私有属性 然后在子类上将该属性重新声明为公共属性或受保护属性 当您创建 Child 类的实例并调用从 Parent 类继承的方法时 使用 Parent 类上的属性 而不是 Child 类上的属性 如果父类上的属性的初始声明是公共
  • 如何在 TinyMCE for Plone 中将 替换为

    我想在 TinyMCE 中用强标签替换粗体标签 如何在 Plone 中使用 Products TinyMCE 做到这一点 我读了TinyMCE文档 http www tinymce com wiki php Configuration va
  • 计算表结果:使用单选按钮值的 PHP switch case

    我编辑了这篇文章以便更好地理解 这是我作为学生实习生的第一个项目 它是一个记录计算机设备的设备监控系统 这是页面中的代码的一部分 该页面包含过滤选项和显示计算机设备列表的表格 过滤选项包含几个单选按钮 它们属于两个类别 状态 和 条件 请参
  • jQuery 连续鼠标按下

    我有以下代码片段 document mousedown function event doSomething 我可以捕获mousedown活动成功 我正在尝试执行以下操作 捕捉第一个mousedown event 我想检测用户是否仍然按住鼠
  • 如何在 C 中使用 printf 打印 unsigned long int ? [复制]

    这个问题在这里已经有答案了 可能的重复 如何在C中打印 unsigned long https stackoverflow com questions 3209909 how to printf unsigned long in gcc 我
  • 为什么我的愿望清单仅限于一项?

    我使用magento 1 9 我的愿望清单有问题 出于某种原因 它只会让我在我的愿望清单中包含一个项目 当我将另一个项目添加到愿望清单时 它会替换我的愿望清单中已有的项目 有配置中没有任何内容来限制愿望清单项目 我只是有点困惑为什么要这样做
  • 如何将 POST 请求重定向到在 MVC 中维护模型值的 url

    我有一个相当标准的排序 过滤器 页面搜索表单 但需要控制 url 的格式 排序 过滤器 页面参数都应该是 url 的一部分 以便可以将地址通过电子邮件发送给某人 当添加另一个过滤器参数时 会发出 POST 请求 我的控制器方法如下所示 Ht
  • RecyclerView 与 ImageView 以及不同的 Activity

    我拥有的 一个 RecyclerView 其中包含酒吧 咖啡店等地方的图片 我想要的 当您单击这些图像之一时 我会向您显示所选地点的信息 我的问题 我该如何设置OnCLickListener例如第三张图片 很多人告诉我在我的 onBindV
  • 如何在matplotlib条形图后面绘制网格线

    x 01 02 02 02 03 02 04 02 05 02 y 2 2 3 7 2 fig ax plt subplots 1 1 ax bar range len y y width 0 3 align center color sk
  • .htaccess 将所有内容重写到父目录

    我正在尝试重写对父目录的每个请求 这是我的 htaccess 的内容 RewriteEngine on RewriteRule 1 不幸的是 这不起作用并导致 错误请求 错误 这可能吗 应该夹在 之间 这样系统就知道它应该将表达式 记忆 为
  • Visual Studio 2017 控制台应用程序:预编译头

    最近我更新了我的 Visual Studio 2017 从那时起 我无法创建 Windows 控制台应用程序项目 文件 新建 项目 我选择了 已安装 Visual C Windows控制台应用程序 当我单击 确定 时 没有出现应用程序设置向
  • 将自定义变量/参数从电子邮件模板传递到 phtml 文件

    我被困在我的自定义代码中 我想将自定义变量从电子邮件模板传递到 pthml 文件 编辑文件 应用程序 代码 本地 法师 销售 模型 Order php 在这个函数中 public function sendNewOrderEmail def
  • linux inotify 事件用于重命名()并覆盖

    我有一个小型应用程序 它监视目录树中特定类型的文件名 monitored 它统计匹配文件的数量 使用 inotify 监视添加或删除匹配文件的各种事件 并可以轮询报告当前文件数量 以及过去几年添加和删除文件的平均速率秒 目录树可以包含数十万
  • 为开源贡献内部工具值得付出努力吗? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 我知道这是一个普遍问题 但我想听听其他人对我们案例的看法 我在一家小公司工作 我们的主要开发工具是 PowerBuilder 这是一个非常有限的
  • OpenCV 的 .Net (dotNet) 包装器? [关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我见过有几个 opencvdotnet http code google com p opencvdo
  • 在 C++ 中是否有通过引用返回新对象实例的正确方法?

    所以我正在编写一些代码 我有这样的代码 class Box private float x y w h public Rectangle GetRect void const return Rectangle x y w h 然后在一些代码
  • boost find in共享内存方法陷入c++多进程项目中

    我正在使用 boost 的 ipc 库来保存复杂的对象 包括图像 在共享内存中 由多个进程使用 我们称这个对象为MyImage 共享内存是一个循环缓冲区 保存了几个MyImage一次对象 在我的代码中 有两个 或更多 进程写入共享内存中的一
Powered by Hwhale