文章目录
1. 基本特性
- 消息队列可以认为是一个消息链表,有足够权限的线程可以往队列里放消息,有足够读权限的线程可以从队列中读取消息。每个消息时一个记录,并且有一个优先级。
- 一个进程向消息队列写入消息之前,并不需要某个进程在该队列上等待该消息的到达,而管道和FIFO如果没有读者就开始写,则会产生SIGPIPE信号
- 管道和FIFO是随进程的持续性,当管道和FIFO最后一次关闭发生时,仍在管道和FIFO中的数据会被丢弃。
消息队列是随内核的持续性,即一个进程向消息队列写入消息后,然后终止,另外一个进程可以在以后某个时刻打开该队列读取消息。只要内核没有重新自举,消息队列没有被删除。
2. API介绍
2.1 创建或打开一个队列:mq_open
// oflag: O_CREAT,O_EXCL,O_WRONLY, O_RDONLY,O_RDWR, O_NONBLOCK
mqd_t mq_open(const char *name, int oflag, ... /*mode_t mode, struct mq_attr *attr*/);
// 成功返回队列描述符,失败返回-1
#include <stdio.h>
#include <stdlib.h>
#include <mqueue.h>
#include <sys/types.h>
#include <fcntl.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main()
{
// /tmp.123 为文件系统中的消息队列名,linux默认在目录/dev/mqueue下
mqd_t mqdes = mq_open("/tmp.123", O_RDWR | O_CREAT, FILE_MODE, NULL);
if (mqdes == -1) {
printf("create mqdes fail\n");
return -1;
}
mq_close(mqdes); // 参数为消息队列描述符,close并不会删除文件系统中的消息队列
return 0;
}
2.2 关闭队列:mq_close
int mq_close(mqd_t mqdes);
// 参数为队列描述符,成功返回0,失败返回-1
mq_close用于关闭一个消息队列,和文件的close类型,关闭后,消息队列并不从系统中删除。一个进程结束,会自动调用关闭打开着的消息队列。
2.3 删除队列:mq_unlink
int mq_unlink(const char *name);
// 参数为消息队列名,成功返回0,失败返回-1
#include <stdio.h>
#include <mqueue.h>
#include <fcntl.h>
int main()
{
if (mq_unlink("/tmp.123") == -1) {
printf("mq_unlink fail\n"); // 参数为文件系统中的消息队列名
return -1;
}
return 0;
}
1)mq_unlink用于删除一个消息队列。消息队列创建后只有通过调用该函数或者是内核自举才能进行删除。 unlink后,需要所有引用该消息队列的进程都close才会真正销毁该消息队列
2)LInux将消息队列描述符实现成了文件描述符,因此在linux下可以用select、poll、epoll来操作消息队列描述符。linux将消息队列挂接在虚拟文件系统中,也可以用rm进行删除
2.4 队列的attr参数:mq_attr
struct mq_attr {
long mq_flags; // 阻塞标志 mq_setattr设置
long mq_maxmsg; // 队列中最大的个数 open时指定
long mq_msgsize; // 每个消息的最大字节数 open时指定
long mq_curmsgs; // 当前队列中的消息数 只能读取
};
mq_setattr可以设置的属性只有mq_flags,用来设置或清除消息队列的非阻塞标志。
mq_maxmsg和mq_msgsize属性只能在创建消息队列时通过mq_open来设置
mq_curmsgs属性只能被获取而不能被设置。
int mq_getattr(mqd_t mqdes, struct mq_attr *attr); // 获取消息队列的属性参数
int mq_setattr(mqd_t mqdes, const struct mq_attr *attr, struct mq_attr *oattr); // 设置消息队列的属性参数
#include<stdio.h>
#include<string.h>
#include<mqueue.h>
#include<fcntl.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
// 设置消息队列的额外参数
int main()
{
mq_attr attrSet;
attrSet.mq_msgsize = 128; // 指定消息队列每个消息的最大长度
attrSet.mq_maxmsg = 10; // 指定消息队列的最大消息个数
mqd_t mqdes = mq_open("/tttmp.111", O_CREAT | O_RDWR, FILE_MODE, &attrSet); // 在open时设置
if (mqdes == -1) {
printf("mq_open error\n");
return -1;
}
char buf[128];
sprintf(buf, "xxxxx");
if (mq_send(mqdes, buf, strlen(buf), 1) == -1)
{
printf("sned fail\n");
return -1;
}
mq_attr attr;
mq_getattr(mqdes, &attr);
printf("%d, %d, %d, %d\n", attr.mq_flags, attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
mq_close(mqdes);
return 0;
}
// 获取消息队列额外参数
int main()
{
mqd_t mqdes = mq_open("/tttmp.111", O_CREAT | O_RDWR, FILE_MODE, NULL);
if (mqdes == -1) {
printf("mq_open error\n");
return -1;
}
mq_attr attr;
mq_getattr(mqdes, &attr);
printf("%d, %d, %d, %d", attr.mq_flags, attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
mq_close(mqdes);
mq_unlink("/tttmp.111");
return 0;
}
2.5 向队列添加消息:mq_send
// 发送的内容为ptr,个数为len,优先级为prio
int mq_send(mqd_t mqdes, const char *ptr, size_t len, unsigned int prio);
// 成功返回0,失败返回-1
#include<stdio.h>
#include<string.h>
#include<mqueue.h>
#include<fcntl.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main()
{
mq_attr attrSet;
attrSet.mq_msgsize = 128;
attrSet.mq_maxmsg = 10;
mqd_t mqdes = mq_open("/tttmp.111", O_CREAT | O_RDWR, FILE_MODE, &attrSet);
if (mqdes == -1) {
printf("mq_open error\n");
return -1;
}
char buf[128];
sprintf(buf, "xxxxx");
if (mq_send(mqdes, buf, strlen(buf), 1) == -1) // 消息队列描述符,需要发送的buf,buf长度,优先级
{
printf("sned fail\n");
return -1;
}
mq_attr attr;
mq_getattr(mqdes, &attr);
printf("%d, %d, %d, %d\n", attr.mq_flags, attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
mq_close(mqdes);
return 0;
}
2.6 接收队列中的消息:mq_receive
// 接受的buf为ptr,buf消息len不小于attr中的mq_msgsize,接收到的消息优先级为priop
ssize_t mq_receive(mqd_t mqdes, char *ptr, size_t len, unsigned int *priop); // 成功返回读取的字节数,失败返回-1
#include<stdio.h>
#include<string.h>
#include<mqueue.h>
#include<fcntl.h>
#define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)
int main()
{
mqd_t mqdes = mq_open("/tttmp.111", O_RDONLY, FILE_MODE, NULL);
if (mqdes == -1) {
printf("mq_open error\n");
return -1;
}
mq_attr attr;
mq_getattr(mqdes, &attr);
unsigned int prip;
printf("%d, %d, %d, %d\n", attr.mq_flags, attr.mq_maxmsg, attr.mq_msgsize, attr.mq_curmsgs);
char buf[128];
while (mq_receive(mqdes, buf, sizeof(buf), &prip) != -1)
{
printf("%s\n", buf);
memset(buf, 0, sizeof(buf));
}
mq_close(mqdes);
return 0;
}
2.7 消息队列由空变有时的异步消息通知:mq_notify
union sigval {
int sival_int;
void *sival_ptr;
};
struct sigevent {
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function_)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
// notification非空时注册异步通知函数,为空时且当前进程之前被注册为接收通知的进程则撤销注册
// 1. 只有一个进程能被注册 2. 只有由空变有的时候通知
// 3. 只有队列没有被阻塞为receive时由空变有才会通知(阻塞优先级高) 4. 每次通知后就清除注册信息
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
2.8 其他
1)两个限制
MQ_PRIO_MAX 消息最大优先级
MQ_OPEN_MAX 一个进程最多能打开的消息队列数量。
#include<stdio.h>
#include<string.h>
#include<mqueue.h>
#include<fcntl.h>
#include <sys/types.h>
#include <unistd.h>
int main(int argc, char **argv)
{
printf("MQ_OPEN_MAX = %ld, MQ_PRIO_MAX = %ld\n",
sysconf(_SC_MQ_OPEN_MAX), sysconf(_SC_MQ_PRIO_MAX));
return 0;
}
总结下
sigsuspend
sigwait
sigwaitinfo
sigtimewait
==》信号API介绍
参考:
https://blog.csdn.net/anonymalias/article/details/9799645
https://www.2cto.com/kf/201608/542972.html