1、前言
1.1 定义
POSIX消息队列是Linux中一种进程间通信机制,允许进程通过发送和接收消息来交换数据。这些消息在队列中按优先级顺序存储和传递。
1.2 应用场景
- 当多个进程需要共享或交换数据时。
- 实现进程间的解耦和异步通信。
- 作为缓冲区,处理速度不同的进程之间的数据传输。
1.3 优缺点
1.3.1 优点
- 灵活,支持多种数据类型和优先级。
- 可靠,消息持久化且不会因发送者崩溃而丢失。
- 高效,支持大量数据的传输和并行处理。
1.3.2 缺点
- 相比管道和信号,API更复杂。
- 可能存在数据复制的开销。
- 需要管理访问权限以确保安全性。
2、常用接口
2.1 mq_open()
打开一个已存在的消息队列,或创建一个新的消息队列。
mqd_t mq_open(const char *name, int oflag, ...);
入参:
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.2 mq_send() 和 mq_timedsend()
向指定的消息队列发送消息。
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
int mq_timedsend(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio, const struct timespec *abs_timeout);
入参:
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.3 mq_receive() 和 mq_timedreceive()
从指定的消息队列接收消息。
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
ssize_t mq_timedreceive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio, const struct timespec *abs_timeout);
入参:
mqdes
:消息队列描述符。msg_ptr
:指向接收消息的缓冲区的指针。msg_len
:接收消息的最大长度。msg_prio
:指向存储接收消息优先级的变量的指针。abs_timeout
(仅mq_timedreceive()
):指向timespec
结构的指针,指定了接收操作的绝对超时时间;如果为NULL,则不设置超时。
返回值:
- 成功时返回接收到的消息长度,失败时返回-1并设置
errno
。
2.4 mq_close()
关闭一个打开的消息队列描述符。
int mq_close(mqd_t mqdes);
入参:
mqdes
:消息队列描述符。
返回值:
- 成功时返回0,失败时返回-1并设置
errno
。
2.5 mq_unlink()
删除一个命名的消息队列。
int mq_unlink(const char *name);
入参:
name
:要删除的消息队列的名称。
返回值:
成功时返回0,失败时返回-1并设置errno
。
2.6 mq_getattr() 和 mq_setattr()
mq_getattr()
获取消息队列的属性;mq_setattr()
设置消息队列的属性,并可以选择性地获取旧的属性。
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
。
3、编程测试
测试代码如下,编译需要加-lrt:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#define QUEUE_NAME "/mesg_p"
#define MAX_SIZE 1024
// 打印时分秒的宏
#define PRINT_MIN_SEC do { \
time_t t = time(NULL); \
struct tm *tm_ptr = localtime(&t); \
printf("%02d:%02d:%02d:", tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec);\
} while (0);printf
int main(int argc, char *argv[])
{
mqd_t mqdes;
char buf[MAX_SIZE] = {0};
struct mq_attr attr;
// 命令行参数
// 第一个参数 S表示发送 R表示接收 D表示删除
if (argc != 2)
{
printf("Usage: %s S|R|D", argv[0]);
return 0;
}
// 设置消息队列属性
attr.mq_flags = 0;
attr.mq_maxmsg = 10;
attr.mq_msgsize = MAX_SIZE;
attr.mq_curmsgs = 0;
// 打开或创建消息队列
mqdes = mq_open(QUEUE_NAME, O_CREAT | O_RDWR, 0644, &attr);
if (mqdes == (mqd_t) -1)
{
perror("mq_open");
exit(1);
}
if (!strcmp(argv[1], "S"))
{
// 发送消息
strcpy(buf, "Mesg 12345678!");
if (mq_send(mqdes, buf, strlen(buf) + 1, 0) == -1)
{
perror("mq_send");
exit(1);
}
PRINT_MIN_SEC("Send: %s\n", buf);
}
else if (!strcmp(argv[1], "R"))
{
// 接收消息
memset(buf, 0, sizeof(buf));
if (mq_receive(mqdes, buf, MAX_SIZE, NULL) == -1)
{
perror("mq_receive");
exit(1);
}
PRINT_MIN_SEC("Received: %s\n", buf);
}
else if (!strcmp(argv[1], "D"))
{
// 删除消息队列
if (mq_unlink(QUEUE_NAME) == -1)
{
perror("mq_unlink");
exit(1);
}
}
else
{
printf("Usage: %s S|R|D", argv[0]);
return 0;
}
// 关闭消息队列
if (mq_close(mqdes) == -1)
{
perror("mq_close");
exit(1);
}
return 0;
}
使用不同的传参完成指定操作,S表示发送 R表示接收 D表示删除,启用两个进程测试数据收发:
测试删除:
4、总结
本文阐述了进程间通信之消息队列(POSIX)的定义、应用场景、优缺点等,列举了编程中使用的接口,编写了测试用例测试相关功能。