【问题标题】:msgrcv not waiting for a message with a specific message type with errno 22 (EINVAL)msgrcv 不等待带有 errno 22 (EINVAL) 的特定消息类型的消息
【发布时间】:2018-08-19 08:00:05
【问题描述】:

我正在尝试使用标准 C 库中的 msgsnd 和 msgrcv 系统调用,但没有获得预期/期望的性能。

我的问题是,当我调用 msgrcv 时,它并没有等待指定 mtype (4) 的消息。

我首先发送一条 mtype 为 1 的消息。这个想法是另一个进程将收到此消息并返回一个 mtype 为 4 的确认消息。从documentation,我希望“如果 msgtyp 是大于零,则接收到第一条 msgtyp 类型的消息”。
我还希望它等到消息类型为 msgtyp 的消息在队列中。 来自文档:

  • 如果未在 msgflg 参数中设置 IPC_NOWAIT 标志,则调用线程会暂停处理,直到发生以下情况之一:
    • 所需类型的消息被发送到消息队列。
    • 消息队列标识符 msqid 已从系统中删除。发生这种情况时,msgrcv() 函数返回值 -1 并将 errno 设置为 EIDRM。
    • 将信号传递给调用线程。发生这种情况时,msgrcv() 函数返回值 -1 并将 errno 设置为 EINTR。

我的 msgrcv 函数没有等待。我在下面创建了一个最小、完整且可验证的示例。

(另请注意,我没有运行任何其他可能会在测试此程序时拦截此消息并使用 mtype = 4 发送回另一条 mtype = 4 的消息的伴随程序)。

#include <sys/msg.h>
#include <iostream>
using namespace std;

int main() {
    int qid = msgget(ftok(".",'u'), IPC_EXCL|IPC_CREAT|0600);
    struct buf {
        long mtype; // required
        int message; // mesg content
    };
    buf msg;
    int size = sizeof(msg)-sizeof(long);

    msg.mtype = 1;
    msgsnd(qid, (struct msgbuf *)&msg, size, 0); // sending
    cout << msg.message << endl;
    //Get acknowledgement from receiverA
    msgrcv(qid, (struct msgbuf *)&msg, size, 4, 0); // read mesg
    cout << msg.message << endl;
}

更新:

msgrcv 函数返回 22 (EINVAL) 的 errno 值,根据文档,这意味着“MessageQueueID 参数不是有效的消息队列标识符。”

所以我继续检查我的 msgget 函数的 errno 值是什么。 msgget 函数返回值 17 (EEXIST)。不确定如何处理此问题,但 msgget() 的文档显示“如果 msgflg 同时指定 IPC_CREAT 和 IPC_EXCL,并且密钥的消息队列已经存在,则 msgget() 失败,errno 设置为 EEXIST。”

只是因为弄乱了 msgget 函数,如果我在 ftok 函数中更改第二个参数以生成不同的标识符,它会起作用,但只有当我运行一次时,如果我尝试使用相同的标识符,错误返回。 所以我想我的问题可以简化为:如何获取我想要使用的消息队列的 qid,但如果它已经存在则不尝试生成它?

【问题讨论】:

  • 调用msgrcv后能打印出errno的值吗?
  • @MinhHoàng 更新了相关错误

标签: c++ message-queue system-calls


【解决方案1】:

尝试在调用msgget时删除IPC_EXCL

来自https://www.tldp.org/LDP/lpg/node34.html

IPC_EXCL

IPC_CREAT 一起使用时,如果队列已存在则失败。

【讨论】:

    【解决方案2】:

    来自手册页:

    如果 msgflg 同时指定 IPC_CREAT 和 IPC_EXCL 以及消息队列 key 已经存在,然后 msgget() 失败,errno 设置为 EEXIST。

    当我第一次尝试执行您的代码时,它在 msgrcv() 上被阻塞,但当我重新执行时它没有。因为在下一次迭代中 msgget() 失败并出现 EXIST 并且 qid 填充为 -1。由于这个 msgsnd() 以及 msgrcv() 失败,参数无效。

    上述问题的解决方法是删除消息队列,在从测试程序返回之前使用msgctl()删除消息队列id,如下:

    msgctl(qid, IPC_RMID, NULL);

    建议:始终检查系统调用的返回值。

    希望这会对你有所帮助。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-06-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-17
      • 2020-07-02
      • 1970-01-01
      • 2011-07-15
      相关资源
      最近更新 更多