【Linux】POSIX 消息队列

前言

通俗易懂的方式来解释 POSIX 消息队列(POSIX Message Queues)。

一、什么是 POSIX 消息队列?

POSIX 消息队列 是一种用于进程间通信(IPC)的技术,它允许不同进程之间通过发送和接收消息来进行通信。POSIX 消息队列是基于 POSIX 标准的一部分,旨在提供跨平台的兼容性和一致性。

通俗解释

想象一下,你和你的朋友在不同的房间里,你们需要传递信息给对方。传统的做法可能是大喊大叫,但这种方式不太可靠。更好的方法是使用一个小盒子(消息队列),你们可以把信息写在纸上,放进这个盒子里。当你有信息要传递给你的朋友时,你可以把纸条放进盒子;当你的朋友有信息给你时,他也同样把纸条放进盒子。你们可以随时去查看这个盒子,看看有没有新的信息。

这就是 POSIX 消息队列的基本思想:

  1. 创建一个消息队列

    • 创建一个类似于“小盒子”的东西,用来存放消息。
  2. 发送消息

    • 当一个进程需要向另一个进程发送消息时,它会把消息放入这个“小盒子”。
  3. 接收消息

    • 接收消息的一方可以从这个“小盒子”中取出消息,并进行处理。

二、如何使用 POSIX 消息队列?

  1. 消息队列描述符 (mqd_t mqd)
    mqd_t 是一个用于表示消息队列描述符数据类型
    mqd_t mqd 声明(定义)一个mqd_t,mqd_t 的变量mqd还没初始化(还没有 = 进行赋值)时,它的值是未定义的。这意味着它的值可能是任何东西,取决于编译器和运行时环境。
    mqd 它是一个不透明类型的整数,用于标识一个打开的消息队列。通过这个描述符,你可以执行与消息队列相关的各种操作,如发送消息、接收消息、关闭消息队列等。

  2. 创建消息队列
    使用 mq_open 函数创建一个消息队列,返回一个消息队列描述符 mqd,
    如果打开或创建成功,mqd它的值为非负整数。
    如果失败,则mqd的值为-1。
    这个函数类似于打开一个文件,但是它实际上创建了一个用于进程间通信的消息队列。

  3. 发送消息
    使用 mq_send 函数将消息发送到消息队列中。

  4. 接收消息
    使用 mq_receive 函数从消息队列中接收消息。

  5. 关闭消息队列
    使用 mq_close 函数关闭消息队列。

  6. 删除消息队列
    使用 mq_unlink 函数删除不再需要的消息队列。

三、函数原型

1. 创建或打开消息队列

使用 mq_open 函数来创建或打开一个消息队列。该函数接受
①队列名称、
②打开标志、
③可选的权限
④属性作为参数。

如果队列不存在且指定了创建标志,将会创建一个新的消息队列。成功创建或打开后,函数返回一个消息队列描述符(mqd_t)。

mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
2. 发送消息

使用 mq_send 函数来发送一个消息到指定的消息队列。该函数接受
①消息队列描述符、
②指向消息的指针、
③消息的大小作为参数。
④发送消息时,可以指定消息的优先级,较高的优先级数值表示较高的优先级。

成功发送后,函数返回 0,否则返回 -1 并设置 errno 来表示错误原因。

int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
3. 接收消息

使用 mq_receive 函数来从指定的消息队列中接收一个消息。该函数接受
①消息队列描述符、
②指向接收缓冲区的指针、
③缓冲区的最大大小作为参数。
④接收消息时,可以选择按优先级接收,也可以选择非阻塞接收

成功接收后,函数返回接收到的消息的大小,否则返回 -1 并设置 errno 来表示错误原因。

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
4. 关闭消息队列

使用 mq_close 函数来关闭一个已打开的消息队列。
该函数接受消息队列描述符作为参数。
关闭消息队列后,相关的资源将被释放。

int mq_close(mqd_t mqdes);
5. 删除消息队列

使用 mq_unlink 函数来删除一个已存在的消息队列。
该函数接受队列名称作为参数。
删除一个消息队列将会移除与之关联的所有消息和状态。

int mq_unlink(const char *name);

四、示例代码

下面是一个完整的示例代码,展示了如何使用上述函数来创建、发送、接收和删除一个消息队列:

创建消息队列
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h> // For mode_t and mode constants

int main() {
    mqd_t mqdes;
    const char *queue_name = "/example_queue";

    // 创建或打开消息队列
    mqdes = mq_open(queue_name, O_CREAT | O_RDWR, 0666, NULL);
    if (mqdes == -1) {
        perror("mq_open");
        return 1;
    }

    // 成功创建或打开消息队列后,可以继续其他操作
    // 例如发送或接收消息
    // mq_send(mqdes, "Hello, World!", 13, 0);
    // mq_receive(mqdes, buffer, sizeof(buffer), NULL);

    // 最终关闭消息队列
    mq_close(mqdes);

    return 0;
}
发送消息
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main() {
    mqd_t mqdes;
    const char *queue_name = "/example_queue";
    const char *msg = "Hello, World!";

    // 打开消息队列
    mqdes = mq_open(queue_name, O_WRONLY);
    if (mqdes == -1) {
        perror("mq_open");
        return 1;
    }

    // 发送消息
    if (mq_send(mqdes, msg, strlen(msg) + 1, 0) == -1) {
        perror("mq_send");
        return 1;
    }

    // 关闭消息队列
    mq_close(mqdes);

    return 0;
}
接收消息
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main() {
    mqd_t mqdes;
    const char *queue_name = "/example_queue";
    char buffer[100];

    // 打开消息队列
    mqdes = mq_open(queue_name, O_RDONLY);
    if (mqdes == -1) {
        perror("mq_open");
        return 1;
    }

    // 接收消息
    ssize_t bytes_received = mq_receive(mqdes, buffer, sizeof(buffer), NULL);
    if (bytes_received == -1) {
        perror("mq_receive");
        return 1;
    }

    printf("Received message: %.*s\n", (int)bytes_received, buffer);

    // 关闭消息队列
    mq_close(mqdes);

    return 0;
}
删除消息队列
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main() {
    const char *queue_name = "/example_queue";

    // 删除消息队列
    if (mq_unlink(queue_name) == -1) {
        perror("mq_unlink");
        return 1;
    }

    return 0;
}

通过以上步骤和示例代码,你可以了解到如何使用 POSIX 消息队列进行进程间通信。如果你有任何进一步的问题或需要更多帮助,请随时提问!

五、总结

POSIX 消息队列是一种简单而强大的进程间通信机制,它通过创建一个“小盒子”(消息队列)来实现不同进程之间的消息传递。这种方式比传统的 IPC 方法(如管道、信号量等)更为灵活和可控,适合需要跨进程传递数据的应用场景。

六、消息队列设备节点

mqueue全称:mqueue 是“message queue”(消息队列)的缩写。
queue_name全称:queue_name 是消息队列的名称。

在 Linux 系统中,POSIX 消息队列通常通过文件系统中的特殊设备节点来表示。这些设备节点通常位于 /dev/mqueue 目录下,格式为 /dev/mqueue/<queue_name>。通过创建和删除消息队列,可以在 /dev/mqueue 目录下看到相应的设备节点的变化。这些设备节点用于标识消息队列,并允许进程通过它们来访问消息队列进行通信。

5.1 创建消息队列设备节点

当你使用 mq_open 函数创建一个消息队列时,系统会在 /dev/mqueue 目录下创建一个对应的设备节点。这个设备节点是一个字符设备文件,用于表示消息队列。

5.2 示例

下面是一个示例,展示了如何创建一个名为 test_queue 的消息队列,并查看对应的设备节点:

5.21 创建消息队列
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/stat.h> // For mode_t and mode constants

int main() {
    mqd_t mqdes;
    const char *queue_name = "/test_queue";

    // 创建或打开消息队列
    mqdes = mq_open(queue_name, O_CREAT | O_RDWR, 0666, NULL);
    if (mqdes == -1) {
        perror("mq_open");
        return 1;
    }

    // 成功创建或打开消息队列后,可以继续其他操作
    // 例如发送或接收消息
    // mq_send(mqdes, "Hello, World!", 13, 0);
    // mq_receive(mqdes, buffer, sizeof(buffer), NULL);

    // 最终关闭消息队列
    mq_close(mqdes);

    return 0;
}
5.22 查看设备节点

运行上面的程序后,你可以通过查看 /dev/mqueue 目录来确认设备节点的存在:

ls -l /dev/mqueue/

输出结果中应该包含一个类似这样的设备节点:

crw-rw---- 1 root mqueue 10, 24 Sep 29 14:00 test_queue

这里的 crw-rw---- 表示这是一个字符设备文件(c),权限为 rw----(读写权限),设备号为 10, 24(主设备号 10,次设备号 24),所有者为 root,组为 mqueue

5.3 设备节点的作用

消息队列设备节点的主要作用是:

  1. 标识消息队列

    • 设备节点提供了一个唯一标识符,用于区分不同的消息队列。
  2. 操作系统管理

    • 操作系统通过设备节点来管理和控制消息队列。
  3. 进程间通信

    • 进程可以通过打开设备节点来访问消息队列,从而实现进程间的通信。
5.4 删除消息队列

当你不再需要一个消息队列时,可以使用 mq_unlink 函数来删除它。删除消息队列后,相应的设备节点也会被移除。

5.41 示例代码
#include <mqueue.h>
#include <errno.h>
#include <stdio.h>
#include <string.h>

int main() {
    const char *queue_name = "/test_queue";

    // 删除消息队列
    if (mq_unlink(queue_name) == -1) {
        perror("mq_unlink");
        return 1;
    }

    return 0;
}

运行这段代码后,再查看 /dev/mqueue 目录,应该发现 test_queue 设备节点已经被删除。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值