1. 简述
消息队列是Linux提供进程间通信的一种方式。当多个进程需要共享或交换数据时,就会用到消息队列。此外,消息队列还可以作为一个缓冲区,可以在处理速度不同的进程间提供数据交换。
2. POSIX消息队列和System V消息队列
与信号量一样,消息队列也有两个不同的版本,分别是POSIX和System V消息队列。
本文着重讲解POSIX消息队列。
3. POSIX消息队列的特点
****(1)标准化:****POSIX消息队列遵循POSIX标准,这意味着它们在支持POSIX的系统上具有一致的接口和行为,从而提高了代码的可移植性。
****(2)消息优先级:****POSIX消息队列支持为每个消息设置优先级。消息队列中的所有消息都会根据它们的优先级进行排序,优先级高的消息会先被消费。
(3)异步通知:POSIX消息队列允许应用程序注册异步通知。当队列中存在消息时,可以通过信号或线程的方式通知应用程序,这样应用程序就不需要不断轮询队列。
****(4)引用计数:****POSIX消息队列使用引用计数机制来管理其生命周期。队列的创建和删除是通过引用计数来控制的,只有当所有引用队列的进程都关闭队列后,队列才会被真正删除。
(5)非阻塞操作:POSIX消息队列支持非阻塞操作。通过设置非阻塞标志,消息队列可以在消息不可用时立即返回,而不是阻塞等待。
****(6)消息格式化:****消息队列中的消息通常包含格式化的数据,这使得它们可以携带复杂的数据结构和信息。
****(7)安全性:****POSIX消息队列提供了权限检查机制,确保只有具有适当权限的进程可以发送或接收消息。
****(8)独立于消息大小:****POSIX消息队列允许发送和接收固定大小的消息,这有助于减少内存碎片和提高性能。
****(9)多线程安全:****POSIX消息队列的操作是线程安全的,可以在多线程环境中使用而不需要额外的同步机制。
4. 提供的API
(1)获取或创建消息队列
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag, /* mode_t mode, struct mq_attr *attr */);
name:消息队列的名称。
oflag:打开或创建队列时的选项标志,可以是以下一个或多个选项的按位或:
O_RDONLY:以只读方式打开消息队列。
O_WRONLY:以只写方式打开消息队列。
O_CREAT:如果指定的消息队列不存在,则创建它。
O_EXCL:与O_CREAT一起使用,如果消息队列已存在,则返回错误。
如果使用了O_CREAT标志,还需要提供两个额外的参数:mode_t mode和struct mq_attr *attr。mode指定新队列的权限,attr指定队列的属性;否则,这两个参数可以省略。
返回值:
成功时返回一个消息队列描述符(mqd_t类型),失败时返回-1并设置errno。
(2)关闭消息队列
int mq_close(mqd_t mqdes);
(3)清除消息队列
int mq_unlink(const char *name);
(4)发送消息
mqd_t mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned msg_prio);
//成功返回0,出错返回-1
mqd_t mq_timedsend(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned msg_prio,
const struct timespec *abs_timeout);
mqdes:消息队列描述符;
msg_ptr:指向消息体缓冲区的指针;
msg_len:消息体的长度,其中mq_receive的该参数不能小于能写入队列中消息的最大大小,即一定要大于等于该队列的mq_attr结构中mq_msgsize的大小。如果mq_receive中的msg_len小于该值,就会返回EMSGSIZE错误。POXIS消息队列发送的消息长度可以为0。
msg_prio:消息的优先级;它是一个小于MQ_PRIO_MAX的数,数值越大,优先级越高。POSIX消息队列在调用mq_receive时总是返回队列中最高优先级的最早消息。如果消息不需要设定优先级,那么可以在mq_send是置msg_prio为0,mq_receive的msg_prio置为NULL。
(5)接收消息
mqd_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned *msg_prio);
//成功返回接收到消息的字节数,出错返回-1
mqd_t mq_timedreceive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned *msg_prio,
const struct timespec *abs_timeout);
参数与mq_send基本一致。
(6)获取和设置队列属性
int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat);
int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat);
mqdes:消息队列描述符。
mqstat:指向mq_attr结构的指针,用于存储或设置消息队列的属性。
omqstat(仅mq_setattr()):如果非NULL,则在此结构中返回旧的队列属性。
返回值:
成功时返回0,失败时返回-1并设置errno。
5. 消息队列例程
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <unistd.h>
#define QUEUE_NAME "/msg_queue"
#define MAX_SIZE 1024
#define MAX_MSG 10
#define MSG_STOP "exit"
int main(int argc, char* argv[])
{
mqd_t mqdes;
char buf[MAX_SIZE];
unsigned int prio;
struct mq_attr attr;
ssize_t nbytes;
/** 设置消息队列属性. */
attr.mq_flags = 0;
attr.mq_maxmsg = MAX_MSG;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
/** 创建消息队列. */
mqdes = mq_open(QUEUE_NAME, O_CREAT | O_WRONLY, 0644, &attr);
if (mqdes == (mqd_t) -1) {
perror("mq_open");
exit(1);
}
/** 发送消息到队列. */
while (1) {
printf("Enter a message to send to the queue (type 'exit' to stop): ");
fgets(buf, MAX_SIZE, stdin);
buf[strcspn(buf, "\n")] = 0; ///< 去掉换行符
if (strcmp(buf, MSG_STOP) == 0) {
break;
}
nbytes = mq_send(mqdes, buf, strlen(buf) + 1, 0);
if (nbytes == -1) {
perror("mq_send");
exit(1);
}
}
/** 关闭消息队列并重新以只读方式打开. */
mq_close(mqdes);
mqdes = mq_open(QUEUE_NAME, O_RDONLY);
if (mqdes == (mqd_t) -1) {
perror("mq_open");
exit(1);
}
/** 从队列中接收消息并打印. */
printf("Reading messages from the queue:\n");
while (1) {
nbytes = mq_receive(mqdes, buf, MAX_SIZE, NULL);
if (nbytes == -1) {
perror("mq_receive");
exit(1);
}
printf("%s\n", buf);
if (strcmp(buf, MSG_STOP) == 0) {
break;
}
}
/** 关闭并删除消息队列. */
mq_close(mqdes);
mq_unlink(QUEUE_NAME);
return 0;
}