前言
通俗易懂的方式来解释 POSIX 消息队列(POSIX Message Queues)。
一、什么是 POSIX 消息队列?
POSIX 消息队列 是一种用于进程间通信(IPC)的技术,它允许不同进程之间通过发送和接收消息来进行通信。POSIX 消息队列是基于 POSIX 标准的一部分,旨在提供跨平台的兼容性和一致性。
通俗解释
想象一下,你和你的朋友在不同的房间里,你们需要传递信息给对方。传统的做法可能是大喊大叫,但这种方式不太可靠。更好的方法是使用一个小盒子(消息队列),你们可以把信息写在纸上,放进这个盒子里。当你有信息要传递给你的朋友时,你可以把纸条放进盒子;当你的朋友有信息给你时,他也同样把纸条放进盒子。你们可以随时去查看这个盒子,看看有没有新的信息。
这就是 POSIX 消息队列的基本思想:
-
创建一个消息队列:
- 创建一个类似于“小盒子”的东西,用来存放消息。
-
发送消息:
- 当一个进程需要向另一个进程发送消息时,它会把消息放入这个“小盒子”。
-
接收消息:
- 接收消息的一方可以从这个“小盒子”中取出消息,并进行处理。
二、如何使用 POSIX 消息队列?
-
消息队列描述符 (mqd_t mqd):
mqd_t
是一个用于表示消息队列描述符的数据类型,
mqd_t mqd
声明(定义)一个mqd_t,mqd_t 的变量mqd还没初始化(还没有 = 进行赋值)时,它的值是未定义的。这意味着它的值可能是任何东西,取决于编译器和运行时环境。
mqd
它是一个不透明类型的整数,用于标识一个打开的消息队列。通过这个描述符,你可以执行与消息队列相关的各种操作,如发送消息、接收消息、关闭消息队列等。 -
创建消息队列:
使用mq_open
函数创建一个消息队列,返回一个消息队列描述符 mqd,
如果打开或创建成功,mqd它的值为非负整数。
如果失败,则mqd的值为-1。
这个函数类似于打开一个文件,但是它实际上创建了一个用于进程间通信的消息队列。 -
发送消息:
使用mq_send
函数将消息发送到消息队列中。 -
接收消息:
使用mq_receive
函数从消息队列中接收消息。 -
关闭消息队列:
使用mq_close
函数关闭消息队列。 -
删除消息队列:
使用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 设备节点的作用
消息队列设备节点的主要作用是:
-
标识消息队列:
- 设备节点提供了一个唯一标识符,用于区分不同的消息队列。
-
操作系统管理:
- 操作系统通过设备节点来管理和控制消息队列。
-
进程间通信:
- 进程可以通过打开设备节点来访问消息队列,从而实现进程间的通信。
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
设备节点已经被删除。