boost :: interprocess :: message_queue必须由写入它的进程创建吗?

时间:2015-05-05 09:59:59

标签: boost boost-interprocess

我有两个进程使用相同的代码相互通信,具体取决于boost 1.58.0的boost :: interprocess :: message_queue。

typedef boost::shared_ptr<boost::interprocess::message_queue> mq_ptr;

mq_ptr m_t2b_queue;
mq_ptr m_b2t_queue;


sink::sink(const char * conn_id, bool is_parent)
{
    const int MAX_MESSAGE_NUMBER = 100000;


    snprintf( m_t2b_queue_name, MAX_MQ_NAME_LEN, "%s_t2b", conn_id);
    snprintf( m_b2t_queue_name, MAX_MQ_NAME_LEN, "%s_b2t", conn_id);

    if( is_parent )
    {
        message_queue::remove("ffqs5hbKgFs_t2b"/*m_t2b_queue_name*/);
        message_queue::remove("ffqs5hbKgFs_b2t"/*m_b2t_queue_name*/);

        permissions perm;
        perm.set_unrestricted();
        m_t2b_queue.reset(new message_queue(create_only, "ffqs5hbKgFs_t2b"/*m_t2b_queue_name*/, MAX_MESSAGE_NUMBER, 24 /*sizeof(mq_item_t)*/, perm));
        m_b2t_queue.reset(new message_queue(create_only, "ffqs5hbKgFs_b2t"/*m_b2t_queue_name*/, MAX_MESSAGE_NUMBER, 24 /*sizeof(mq_item_t)*/, perm));
    }
    else
    {
        m_t2b_queue.reset(new message_queue(open_only, "ffqs5hbKgFs_t2b"/*m_t2b_queue_name*/));
        m_b2t_queue.reset(new message_queue(open_only, "ffqs5hbKgFs_b2t"/*m_b2t_queue_name*/));
        printf( "t2b max msg size = %d\n", m_t2b_queue->get_max_msg_size() );
        printf( "b2t max msg size = %d\n", m_b2t_queue->get_max_msg_size() );
    }    
}

上述代码中的变量已替换为硬编码值,因此更清晰

父进程创建接收器实例,如

sink parent( "ffqs5hbKgFs", true);

通过这种方式,我认为父进程在内部创建了2个message_queue。一个用于读取/另一个用于写入。

然后父进程创建子进程,创建像

这样的接收器实例
sink child( "ffqs5hbKgFs", false);

我想子进程打开父进程创建的现有2 message_queue。并使用其中一个向父进程发送消息。

问题是,子进程中的message_queue已成功打开,但其max_msg_size为零。

t2b max msg size = 0
b2t max msg size = 0

当clild进程尝试通过message_queue发送消息时,这会导致错误。

问题:这是否意味着必须由写入它的进程创建message_queue

在我的场景中,我希望message_queue总是由父进程创建,这样当子进程崩溃时,可以将新的子进程附加到现有的message_queue以恢复作业。

原因:最后我找到了原因,一个进程正在运行为x86。另一个是以x64

运行

1 个答案:

答案 0 :(得分:0)

您需要在队列创建期间同步访问权限。您实际上正在同步文件系统访问。

在这里您可以看到正在复制的失败:

<强> Compiling On Coliru

int main() {
    std::cout << "Before: " << getpid() << "\n";

    if (int child = fork()) {
        std::cout << "Parent: " << getpid() << "\n";
        sink parent("ffqs5hbKgFs", true);

        int status;
        waitpid(child, &status, 0);
    } else {
        std::cout << "Child: " << getpid() << "\n";
        sink parent("ffqs5hbKgFs", false);
    }
}

打印

terminate called after throwing an instance of 'boost::interprocess::interprocess_exception'
what():  No such file or directory

当然,一个简单的睡眠会证明它是一个竞争条件(你期望什么,孩子将打开队列,而父母只是remove - 他们):

} else {
    std::cout << "Child: " << getpid() << "\n";
    sleep(1); // one second
    sink parent("ffqs5hbKgFs", false);
}

打印,例如:

Before: 3318
Child: 3319
t2b max msg size = 24
b2t max msg size = 24
Parent: 3318

进一步阅读

要获得正确的同步解决方案,请参阅the docs

  

如前所述,如果无法有效地同步对该内存的访问,则通过内存映射文件或共享内存对象在进程之间共享内存的能力不是很有用。这与线程同步机制发生的问题相同,其中堆内存和全局变量在线程之间共享,但是对这些资源的访问通常需要通过互斥和条件变量进行同步。 Boost.Threads在同一进程内的线程之间实现这些同步实用程序。 Boost.Interprocess实现了类似的机制来同步来自不同进程的线程

完整演示

<强> Compiling On Coliru

#include <boost/shared_ptr.hpp>
#include <boost/make_shared.hpp>
#include <boost/interprocess/ipc/message_queue.hpp>
#include <iostream>
#include <mqueue.h>

namespace bip = boost::interprocess;

typedef boost::shared_ptr<boost::interprocess::message_queue> mq_ptr;

struct mq_item_t {
    char data[24];
};

struct sink {
    std::string m_t2b_queue_name;
    std::string m_b2t_queue_name;
    mq_ptr m_t2b_queue;
    mq_ptr m_b2t_queue;

    sink(const char * conn_id, bool is_parent)
        : 
        m_t2b_queue_name(conn_id + std::string("_t2b")),
        m_b2t_queue_name(conn_id + std::string("_b2t"))
    {
        const int MAX_MESSAGE_NUMBER = 100000;

        if( is_parent )
        {
            bip::message_queue::remove(m_t2b_queue_name.c_str());
            bip::message_queue::remove(m_b2t_queue_name.c_str());

            bip::permissions perm;
            perm.set_unrestricted();
            m_t2b_queue.reset(new bip::message_queue(bip::create_only, m_t2b_queue_name.c_str(), MAX_MESSAGE_NUMBER, sizeof(mq_item_t), perm));
            m_b2t_queue.reset(new bip::message_queue(bip::create_only, m_b2t_queue_name.c_str(), MAX_MESSAGE_NUMBER, sizeof(mq_item_t), perm));
        }
        else
        {
            m_t2b_queue.reset(new bip::message_queue(bip::open_only, m_t2b_queue_name.c_str()));
            m_b2t_queue.reset(new bip::message_queue(bip::open_only, m_b2t_queue_name.c_str()));
            std::cout << "t2b max msg size = " << m_t2b_queue->get_max_msg_size() << "\n";
            std::cout << "b2t max msg size = " << m_b2t_queue->get_max_msg_size() << "\n";
        }    
    }
};

#include <sys/types.h>
#include <sys/wait.h>

int main() {
    std::cout << "Before: " << getpid() << "\n";

    if (int child = fork()) {
        std::cout << "Parent: " << getpid() << "\n";
        sink parent("ffqs5hbKgFs", true);

        int status;
        waitpid(child, &status, 0);
    } else {
        std::cout << "Child: " << getpid() << "\n";
        sleep(1); // one second
        sink parent("ffqs5hbKgFs", false);
    }
}