linux POSIX消息队列详解&使用&demo
系统调用
mq_open()
打开一个消息队列,
#include <fcntl.h> /* For O_* constants */
#include <sys/stat.h> /* For mode constants */
#include <mqueue.h>
mqd_t mq_open(const char *name, int oflag);
mqd_t mq_open(const char *name, int oflag, mode_t mode, struct mq_attr *attr);
//编译时需要链接rt库 即 -lrt
//name 为消息队列的标志, 使用sample /somaname ,要加斜杠,详细问男人,并且长度不可以大于NMAE_MAX(i.e., 255)
//oflag 位掩码,比如不包含O_CREAT,就是打开一个已有的,包含就是创建
//mode mode参数指定要放置在新队列上的权限,具体可以参考(man open 2)。(权限位的符号定义可通过包含(sys/stat.h)来获得)权限设置针对进程umask进行屏蔽。
//attr参数是一个 mq_attr 结构,它指定了新消息队列的特性。如果 attr 为 NULL,那么将使用实现定义的默认特性创建队列
//attr mq_open mq_getattr mq_setattr 都会用到它,含了与一个消息描述符相关联的打开的消息队列描述的相关信息以及该描述符所引用的队列的相关信息。maxmsg,mq_msgsize 可以在创建时指定消息数量和大小,并且之后无法被修改。如果省略,会使用/proc/sys/fs/mqueue的默认参数
struct mq_attr {
long mq_flags; /* Flags (ignored for mq_open()) */
long mq_maxmsg; /* Max. # of messages on queue */
long mq_msgsize; /* Max. message size (bytes) */
long mq_curmsgs; /* # of messages currently in queue
(ignored for mq_open()) */
};
其中oflag的详细选项为
标记 | 描述 |
---|---|
O_CREAT | 队列不存在时创建队列 |
O_EXCL | 与 O_CREAT 一起使用,若消息队列已存在,则错误返回 |
O_RDONLY | 只读打开 |
O_WRONLY | 只写打开 |
O_RDWR | 读写打开 |
O_NONBLOCK | 以非阻塞模式打开 |
mq_send()
发送消息
#include <mqueue.h>
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned int msg_prio);
//成功返回0,失败-1
//mqdes为mq_open的返回值,msg_ptr是你要发送的buf, msg_len为长度, 必须小于attr中的mq_msgsize,否则会返回EMSGSIZE错误
//msg_proi为消息优先级,队列存在优先级,如果为0代表取优先级最高的,如果要取的优先级不存在也会取最高的
//如果队列满了,阻塞模式下会一直阻塞,非阻塞模式直接返回错误
mq_receive()
从消息队列中接收消息
#include <mqueue.h>
ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned int *msg_prio);
//mqdes为mq_open的返回值
//msg_len代表msg_ptr 指向的缓冲区中的可用字节数。
//msg_proi为消息优先级 详情见上面mq_open注释
mq_getattr()
获取一个消息队列相关联的attr.
#include<mqueue.h>
int mq_getattr(mqd_t mqdes,struct mq_attr *attr);
mq_setattr()
修改一个消息队列相关联的attr,
#include<mqueue.h>
int mq_setattr(mq_t mqdes,const struct mq_attr *newattr,struct mq_attr *oldattr);
//使用newattr中的mq_flags字段替换oldattr中的mq_flags
//当前消息队列消息个数只能获取,消息队列的容量和单个消息长度只能在创建的时候设置
mq_notify()
用于通知队列不为空的异步通知, 即receive无需阻塞或者定期receive
#include<mqueue.h>
int mq_notify(mqd_t mqdes, const struct sigevent *notification);
// 成功返回0,失败返回-1
struct sigevent
{
int sigev_notify;
int sigev_signo;
union sigval sigev_value;
void (*sigev_notify_function)(union sigval);
pthread_attr_t *sigev_notify_attributes;
};
其中sigev_notify可选值为
值 | |
---|---|
SIGEV_NONE | 什么也不做 |
SIGEV_SIGNAL | 将sigev_signo指定的信号发送给指定的进程 |
SIGEV_SIGEV_THREAD | 事件发生时,内核会(在此进程内)以sigev_notify_attributes为线程属性创建一个线程,并让其执行sigev_notify_function,并以sigev_value为其参数 |
tip:(这一段在网上cp的)
- 在任何一个时刻都只有一个进程能够向一个特定的消息队列注册接收通知。如果一个消息队列上已经存在注册进程了,那么后续在该队列上的注册请求将会失败。
- 只有当一条新消息进入之前为空的队列时注册进程才会收到通知。如果在注册的时候队列中已经包含消息,那么只有当队列被清空之后有一条新消息达到之时才会发出通知。
- 当向注册进程发送了一个通知之后就会删除注册信息,之后任何进程就能够向队列注册接收通知了。当通知被发送给注册进程时,注册即被撤销,该进程若要重新注册,则必须重新调用 mq_notify()。
- 注册进程只有在当前不存在其他在该队列上调用 mq_receive() 而发生阻塞的进程时才会收到通知。如果其他进程在 mq_receive() 调用中被阻塞了,那么该进程会读取消息,注册进程会保持注册状态。
- 一个进程可以通过在调用 mq_notify() 时传入一个值为 NULL 的 notification 参数来撤销自己在消息通知上的注册信息。
mq_close()
关闭消息队列描述符
#include<mqueue.h>
int mq_close(mqd_t mqdes);
与文件上的 close() 一样,关闭一个消息队列并不会删除该队列。要删除请使用mq_unlink
mq_unlink()
mq_unlink() 函数删除通过 name 标识的消息队列,并将队列标记为在所有进程使用完该队列之后销毁该队列。这里其他进程使用通过引用计数判断
#include<mqueue.h>
int mq_unlink(const char *name);
demo
编译时请指定链接rt库 -lrt
发送端:
//send.cpp
#include <stdio.h>
#include <iostream>
#include <stdlib.h>
#include <cstring>
#include <fcntl.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <unistd.h>
#define MSG2 "hhhh"
#define MODE (S_IRWXU | S_IRWXG | S_IRWXO)
int main() {
char *buf;
size_t buf_len = 1024;
unsigned int prio = 0;
mqd_t mqd;
struct mq_attr attr;
memset(&attr, 0, sizeof(attr));
attr.mq_maxmsg = 3;
attr.mq_msgsize = 1024;
//open
mqd = mq_open("/tmp", O_RDWR | O_CREAT, MODE, &attr);
if(mqd == (mqd_t)-1) {
perror("mq_open");
exit(-1);
}
buf = (char *)malloc(buf_len);
if(!buf) {
perror("malloc");
exit(-1);
}
strncpy(buf, "MSG2", sizeof(MSG2));
while(1) {
mq_send(mqd, buf, buf_len, 0);
sleep(1);
}
free(buf);
//close
mq_close(mqd);
//unlink
//mq_unlink("/tmp");
return 0;
}
接收端:
//receive.cpp
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <mqueue.h>
#include <cstring>
int main() {
char *buf;
long buf_len;
mqd_t mqd;
mqd = mq_open("/tmp", O_RDONLY);
if(mqd == (mqd_t)-1) {
perror("mq_open");
exit(-1);
}
struct mq_attr attr;
memset(&attr, 0, sizeof(attr));
mq_getattr(mqd, &attr);
buf_len = attr.mq_msgsize;
buf = (char *)malloc(buf_len);
unsigned int prio = 0;
//receive
memset(buf, 0, sizeof(buf));
while(1) {
mq_receive(mqd, buf, buf_len, &prio);
printf("msg :%s, cur size is %ld\n", buf, attr.mq_curmsgs);
}
return 0;
}