消息队列可以认为是一个链表。进程(线程)可以往里写消息,也可以从里面取出消息。一个进程可以往某个消息队列里写消息,然后终止,另一个进程随时可以从消息队列里取走这些消息。这里也说明了,消息队列具有随内核的持续性,也就是系统不重启,消息队列永久存在。
3.2.1 创建(并打开)、关闭、删除一个消息队列
/* For O_* constants */ /* For mode constants */ 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); /* 函数说明:函数创建或打开一个消息队列,创建后,在/dev/mqueque下可以看到对应文件 返回值:成功返回消息队列描述符,失败返回-1,错误原因存于errno中 name: 文件路径,必须以/开头,而且有且只有一个/ oflag: 与open的打开标志一样,加上O_CREAT标志,能够创建消息队列 mode: 与open的打开mode一样 attr: 创建消息队列的时候需要,指定消息队列大小消息个数等,为NULL表示使用默认参数 */ /*消息队列属性结构体*/ struct mq_attr { long mq_flags; /* Flags: 0 or O_NONBLOCK */ long mq_maxmsg; /* Max. # of messages on queue */ long mq_msgsize; /* Max. message size (bytes) */ long mq_curmsgs; /* # of messages currently in queue */ }; mqd_t mq_close(mqd_t mqdes); /* 函数说明:关闭一个打开的消息队列,表示本进程不再对该消息队列读写 返回值:成功返回0,失败返回-1,错误原因存于errno中 */ int mq_unlink(const char *name); /* 函数说明:删除一个消息队列,好比删除一个文件,其他进程再也无法访问 返回值:成功返回0,失败返回-1,错误原因存于errno中 */
需要注意:
消息队列的名字最好使用“/”打头,并且只有一个“/”的名字。否则可能出现移植性问题;它必须符合已有的路径名规则(最多由PATH_MAX个字节构成,包括结尾的空字节)。
3.2.2 Posix消息队列读写
消息队列的读写主要使用下面两个函数:
int mq_send(mqd_t mqdes, const char *msg_ptr, size_t msg_len, unsigned msg_prio); /* 功能:发送一条消息到队列 mqdes:消息队列文件描述符 msg_ptr: 消息内容 msg_len: 内容长度 msg_prio: 消息优先级,它是一个小于MQ_PRIO_MAX的数, 数值越大,优先级越高 返回:若成功则为消息中字节数,若出错则为-1 */ ssize_t mq_receive(mqd_t mqdes, char *msg_ptr, size_t msg_len, unsigned *msg_prio); /* 功能:从队列接收一条消息 mqdes:消息队列文件描述符 msg_ptr: 消息内容 msg_len: 内容长度 msg_prio: 消息优先级 返回:若成功则为0, 若出错则为-1 */
源码sendmq.c
void error_print(const char* msg) { perror(msg); exit(-1); } /*向消息队列发送消息,消息队列名及发送的信息通过参数传递*/ int main(int argc, char *argv[]) { const char* mqname = "/mymq"; char buf[] = "helloworld"; mqd_t mqd; int ret; //只写模式找开消息队列,不存在就创建 mqd = mq_open(mqname, O_WRONLY | O_CREAT, 0666, NULL); if(mqd < 0) error_print("mq_open"); /*向消息队列写入消息,如消息队列满则阻塞,直到消息队列有空闲时再写入*/ ret = mq_send(mqd, buf, strlen(buf) + 1, 10); if(ret < 0) error_print("mq_send"); ret = mq_close(mqd); if(ret < 0) error_print("mq_close"); return 0; }
编译:
gcc -o sendmq sendmq.c -lrt
运行后可以在/dev/mqueue下看到mymq文件。
源码recvmq.c
void error_print(const char* msg) { perror(msg); exit(-1); } /*读取某消息队列,消息队列名通过参数传递*/ int main(int argc, char *argv[]) { const char* mqname = "/mymq"; mqd_t mqd; struct mq_attr attr; char *buf; unsigned int prio; int ret; /*只读模式打开消息队列*/ mqd = mq_open(mqname, O_RDONLY); if(mqd < 0) error_print("mq_open"); /*取得消息队列属性,根据mq_msgsize动态申请内存*/ ret = mq_getattr(mqd, &attr); if(ret < 0) error_print("mq_getattr"); /*动态申请保证能存放单条消息的内存*/ buf = (char*)malloc(attr.mq_msgsize); if(NULL == buf) error_print("malloc"); /*接收一条消息*/ ret = mq_receive(mqd, buf, attr.mq_msgsize, &prio); if(ret < 0) error_print("receive"); printf("read data %s priority %u\n", buf, prio); ret = mq_close(mqd); if(ret < 0) error_print("mq_close"); /*消息队列使用完后就可以删除 ret = mq_unlink(mqname); if(ret < 0) error_print("mq_unlink"); */ free(buf); return 0; }
编译并执行:
gcc -o recvmq recvmq.c -lrt
需要注意的是:当消息不断发送,达到消息队列容量最大值的时候,mq_send将阻塞,知道消息队列被接收走,如果消息还未接收,就把消息队列文件删除,则消息丢失。
3.2.3 消息队列的属性
Posix消息队列的属性使用如下结构存放: struct mq_attr { long mq_flags; /*阻塞标志位,0为非阻塞(O_NONBLOCK)*/ long mq_maxmsg; /*队列所允许的最大消息条数*/ long mq_msgsize; /*每条消息的最大字节数*/ long mq_curmsgs; /*队列当前的消息条数*/ }; /* 队列可以在创建时由mq_open()函数的第四个参数指定mq_maxmsg,mq_msgsize。 如创建时没有指定则使用默认值,一旦创建,则不可再改变。 队列可以在创建后由mq_setattr()函数设置mq_flags */ /*取得消息队列属性,放到mqstat中*/ int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); /*设置消息队列属性,设置值由mqstat提供,原先值写入omqstat中,只是用来改变O_NONBLOCK标志*/ int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat, struct mq_attr *omqstat); 均返回:若成功则为0,若出错为-1
获取消息队列的属性:
attrmq.c
void error_print(const char* msg) { perror(msg); exit(-1); } int main() { mqd_t mqd; int ret; struct mq_attr mqattr; const char* mqname = "/mymq"; /*只读模式打开消息队列*/ mqd = mq_open(mqname, O_CREAT | O_RDONLY, 0666, NULL); if(mqd < 0) error_print("mq_open"); /*取得消息队列属性*/ ret = mq_getattr(mqd, &attr); if(ret < 0) error_print("mq_getattr"); printf("nonblock flag:%ld\n", attr.mq_flags); printf("max msgs:%ld\n", attr.mq_maxmsg); printf("max msg size:%ld\n", attr.mq_msgsize); printf("current msg count:%ld\n", attr.mq_curmsgs); ret = mq_close(mqd); if(ret < 0) error_print("mq_close"); ret = mq_unlink(mqname); if(ret < 0) error_print("mq_unlink"); return 0; }
编译并执行:
gcc -o attrmq attrmq.c -lrt
设置消息队列的属性:
源码setarrtmq.c
void error_print(const char* msg) { perror(msg); exit(-1); } int main() { mqd_t mqd; int ret; const char * mqname = "/mymq"; struct mq_attr mqattr; mqattr.mq_maxmsg = 10; mqattr.mq_msgsize = 8192; mqd = mq_open(mqname, O_RDWR | O_CREAT, 0666, &mqattr); if(mqd < 0) { perror("mq_open"); exit(1); } mqattr.mq_flags = O_NONBLOCK; mq_setattr(mqd, &mqattr, NULL);// mq_setattr()只关注mq_flags /*取得消息队列属性*/ ret = mq_getattr(mqd, &mqattr); if(ret < 0) error_print("mq_getattr"); printf("nonblock flag:%ld\n", mqattr.mq_flags); printf("max msgs:%ld\n", mqattr.mq_maxmsg); printf("max msg size:%ld\n", mqattr.mq_msgsize); printf("current msg count:%ld\n", mqattr.mq_curmsgs); ret = mq_close(mqd); if(ret < 0) error_print("mq_close"); return 0; }
编译运行:
gcc -o setattrmq setattrmq.c -lrt
消息队列系统限制查看:
cat /proc/sys/fs/mqueue/msg_max #查看消息队列的消息最大长度 cat /proc/sys/fs/mqueue/msgsize_max #查看消息队列的消息最大个数