msgrcv 不等待带有错误号 22 (EINVAL) 的特定消息类型的消息

msgrcv not waiting for a message with a specific message type with errno 22 (EINVAL)

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

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

我首先发送一条 mtype 为 1 的消息。我的想法是,另一个进程将收到此消息并发送回一条 mtype 为 4 的确认消息。从 documentation,我预计"if msgtyp is greater than zero, the first message of type msgtyp is received".
我还希望它等到消息类型为 msgtyp 的消息出现在队列中。 来自文档:

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

(另请注意,在测试此程序时,我不会 运行 宁其任何其他可能使用 mtype = 1 拦截此消息并发回 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 函数 returning errno 值为 22 (EINVAL),根据文档,这意味着 "The MessageQueueID parameter is not a valid message queue identifier."

所以我继续检查我的 msgget 函数的 errno 值是多少。 msgget 函数 returns 的值为 17 (EEXIST)。不知道该怎么办,但是 msgget() 的文档说 "If msgflg specifies both IPC_CREAT and IPC_EXCL and a message queue already exists for key, then msgget() fails with errno set to EEXIST."

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

尝试在调用 msgget 时删除 IPC_EXCL

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

IPC_EXCL

When used with IPC_CREAT, fail if queue already exists.

来自手册页:

If msgflg specifies both IPC_CREAT and IPC_EXCL and a message queue already exists for key, then msgget() fails with errno set to EEXIST.

当我第一次尝试执行您的代码时,它在 msgrcv() 上阻塞了,但是当我 re-executed 时却没有。因为在下一次迭代中,msgget() 失败并返回 EXIST 并且 qid 填充为 -1。由于此 msgsnd() 以及 msgrcv() 因参数无效而失败。

上述问题的解决方案是删除消息队列,在从测试程序 returning 之前使用 msgctl() 删除消息队列 ID,如下所示:

msgctl(qid, IPC_RMID, NULL);

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

希望对您有所帮助。