POSIX 消息队列

POSIX 消息队列可以认为是一个消息链表。进程(线程)可以往里写消息,也可以从里面取出消息。可以在不相关的进程之间发送和接收数据。

创建(打开)消息队列-mq_open()函数

mq_open()函数用于打开或创建一个消息队列,该函数定义如下:

#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);

参数说明

  • name:消息队列的名称,形式为一个斜杠 "/" 开头的字符串,类似于文件路径。
  • oflag:打开标志,用于指定消息队列的打开方式,可以是以下之一或它们的组合:
    • O_RDONLY:只读方式打开消息队列。
    • O_WRONLY:只写方式打开消息队列。
    • O_RDWR:读写方式打开消息队列。
    • O_CREAT:如果消息队列不存在,则创建它。
    • O_EXCL:与 O_CREAT 同时使用,确保创建一个新的消息队列。
  • mode:用于指定创建的消息队列的操作权限。类似于 chmod 命令中的权限设置。
  • attr:指向结构体 mq_attr 的指针,指定消息队列的属性,如最大消息数、消息大小等。

返回值

  • 如果函数执行成功,返回一个非负整数,表示消息队列的标识符(文件描述符)。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

注意事项

  • 消息队列的名称在系统中必须是唯一的,不同进程可以通过相同名称打开同一个消息队列。
  • 消息队列的名称需要以斜杠 "/" 开头,必须符合路径名规则(最多由PATH_MAX个字节构成,包括结尾的空字节)。
  • 创建的消息队列以文件形式保存在 /dev/mqueque 目录下。

用法

 1 #include<stdio.h>
2 #include<fcntl.h>
3 #include<mqueue.h>
4 #include<sys/stat.h>
5
6 int main(int argc, char** argv)
7 {
8 mqd_t mqd = mq_open("/myqueue", O_RDONLY | O_CREAT | O_EXCL, 0666, NULL);
9 if(mqd == -1)
10 {
11 perror("mq_open");
12 return -1;
13 }
14
15 return 0;
16 }

如果代码中使用了 POSIX 消息队列相关代码,在编译时,需要指定额外的库文件,例如:

gcc my_mqueue.c -o a.out -lrt

输出:

$ ./a.out
$ ls -l /dev/mqueue/
总用量 0
-rw-rw-r-- 1 test test 80 12月 21 10:04 myqueue

关闭消息队列-mq_close()函数

mq_close()函数用于关闭一个已经打开的消息队列描述符,该函数定义如下:

#include <mqueue.h>

int mq_close(mqd_t mqdes);

参数说明

  • mqdes:待关闭的消息队列描述符。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

删除消息队列-mq_unlink()函数

mq_unlink()函数用于删除已经存在的消息队列,即删除消息队列的名称。该函数定义如下:

#include <mqueue.h>

int mq_unlink(const char *name);

参数说明

  • mqdes:要删除的消息队列的名称。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

注意事项

  • 如果在调用 mq_unlink() 函数之前,没有其他进程打开该消息队列,那么该消息队列将被立即删除,并且所有之前打开的描述符将成为无效。
  • 如果有其他进程打开了该消息队列,但没有对该消息队列进行写入或读取操作,那么调用 mq_unlink() 函数后,对该消息队列的访问将立即被阻止,但消息队列本身将保持存在。
  • 如果有其他进程当前正在使用该消息队列(执行了读写操作),删除操作将会延迟到所有打开的描述符都关闭或相应进程终止时才会生效。

消息队列的属性

struct mq_attr结构体

struct mq_attr结构体用于描述 POSIX 消息队列属性,包含如下成员:

struct mq_attr {
long mq_flags;
long mq_maxmsg;
long mq_msgsize;
long mq_curmsgs;
};

各个成员的含义如下:

  • mq_flags:标志位,取值为0 或者 O_NONBLOCK(非阻塞)
  • mq_maxmsg: 是消息队列中允许的最大消息数,默认是10。
  • mq_msgsize:消息队列中消息的最大大小,单位是字节,默认是8192字节。
  • mq_curmsgs:输出参数,当前队列中的消息数。

查看消息队列默认值:

cat /proc/sys/fs/mqueue/msg_max     #查看消息队列的消息最大长度
cat /proc/sys/fs/mqueue/msgsize_max #查看消息队列的消息最大个数

在消息队列的四个属性中:

  • mq_curmsgs只能获取不能设置。
  • mq_flags只能通过 mq_setattr() 函数设置,该函数的唯一作用就是设置或清除非阻塞标志。
  • mq_maxmsg 和 mq_msgsize 只能在创建新队列时由 mq_open() 函数的 attr 参数设置。
  • mq_maxmsg 和 mq_msgsize 必须同时指定,否则 mq_open() 函数创建新队列会失败。
  • O_NONBLOCK 标识不能通过 mq_flags 指定,可以通过 mq_open() 函数 oflag 参数指定,如果想要移除,通过 mq_setattr() 函数设置。

获取消息队列属性-mq_getattr()函数

mq_getattr()函数用于获取消息队列的属性信息,该函数定义如下:

#include <mqueue.h>

int mq_getattr(mqd_t mqdes, struct mq_attr *mqstat); 

参数说明

  • mqdes:要获取属性的消息队列描述符。
  • mqstat:指向 struct mq_attr 结构的指针,用于存储获取的属性信息。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

设置消息队列属性-mq_setattr()函数

mq_setattr()函数用于设置消息队列的属性,该函数定义如下:

#include <mqueue.h>

int mq_setattr(mqd_t mqdes, const struct mq_attr *mqstat,
struct mq_attr *omqstat);

参数说明

  • mqdes:要设置属性的消息队列描述符。
  • mqstat:指向 struct mq_attr 结构的指针,指向新的属性信息。
  • omqstat:指向 struct mq_attr 结构的指针,保存原先的属性信息。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。

消息发送与接收

消息发送-mq_send()函数

mq_send() 函数用于向消息队列发送消息,该函数定义如下:

#include <mqueue.h>

int mq_send(mqd_t mqdes, const char *msg_ptr,
size_t msg_len, unsigned int msg_prio);

参数说明

  • mqdes:消息队列描述符。
  • msg_ptr:指向要发送的消息的指针。
  • msg_len:要发送的消息的长度,不能超过消息队列的最大长度。
  • msg_prio:消息的优先级, 数值越大,优先级越高。优先级低的消息会排在优先级高的消息之后。

返回值

  • 如果函数执行成功,返回0。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
  • 如果消息队列已满,mq_send() 函数会阻塞直到有空间可用。
  • 如果设置了非阻塞标志,此时会立即返回,错误码为 EAGAIN。

消息接收-mq_receive函数

mq_receive() 函数用于从消息队列接收消息。该函数定义如下:

#include <mqueue.h>

ssize_t mq_receive(mqd_t mqdes, char *msg_ptr,
size_t msg_len, unsigned int *msg_prio);

参数说明

  • mqdes:消息队列描述符。
  • msg_ptr:指向接收消息的缓冲区的指针,用于存储接收到的消息。
  • msg_len:接收消息的最大长度,必须大于等于消息的实际长度。
  • msg_prio:用于存储接收到的消息的优先级。

返回值

  • 如果函数执行成功,返回接收到的消息的长度(即实际读取的字节数)。
  • 如果发生错误,返回 -1。可以通过 errno 变量来获取具体的错误信息。
  • 如果消息队列为空,mq_receive() 函数会阻塞直到有消息可用。
  • 如果设置了非阻塞标志,此时会立即返回,错误码为 EAGAIN。

示例

进程A负责读取:

 1 #include<stdio.h>
2 #include<sys/stat.h>
3 #include<mqueue.h>
4 #include<fcntl.h>
5 #include<errno.h>
6 #include<unistd.h>
7 #include<stdlib.h>
8
9 int main(int argc, char** argv)
10 {
11 mqd_t mqd = mq_open("/my_mqueue", O_RDONLY);
12 if(mqd == -1)
13 {
14 perror("mq_open");
15 return -1;
16 }
17
18 struct mq_attr attr;
19 if(mq_getattr(mqd, &attr) == -1)
20 {
21 perror("mq_getattr");
22 return -1;
23 }
24
25 char* pBuf = (char*)malloc(attr.mq_msgsize);
26 if(pBuf == NULL)
27 {
28 perror("malloc");
29 return -1;
30 }
31
32 int prio;
33 while(1)
34 {
35 int ret = mq_receive(mqd, pBuf,attr.mq_msgsize, &prio);
36 if(ret == -1)
37 {
38 if(errno == EAGAIN)
39 {
40 printf("no block, contimue to read\n");
41 continue;
42 }
43 perror("mq_receive");
44 return -1;
45 }
46 pBuf[ret] = '\0';
47 printf("read data : %s, prio : %d\n", pBuf, prio);
48 sleep(1);
49 }
50
51 free(pBuf);
52 return 0;
53 }

进程B负责写入:

 1 #include<stdio.h>
2 #include<stdlib.h>
3 #include<string.h>
4 #include<unistd.h>
5 #include<sys/stat.h>
6 #include<errno.h>
7 #include<mqueue.h>
8
9 int main(int argc, char** argv)
10 {
11 struct mq_attr attr0;
12 //attr0.mq_flags = O_NONBLOCK; //在此处指定无效
13 attr0.mq_maxmsg = 5;
14 attr0.mq_msgsize = 4096;
15 mqd_t mqd;
16
17 AGAIN:
18 mqd = mq_open("/my_mqueue", O_WRONLY | O_CREAT | O_EXCL /*| O_NONBLOCK*/, 0666, &attr0);
19 if(mqd == -1)
20 {
21 if(errno == EEXIST)
22 {
23 mq_unlink("/my_mqueue");
24 goto AGAIN;
25 }
26 perror("mq_open");
27 return -1;
28 }
29
30 struct mq_attr attr;
31 if(mq_getattr(mqd, &attr) == -1)
32 {
33 perror("mq_getattr");
34 return -1;
35 }
36
37 char* pBuf = (char*)malloc(attr.mq_msgsize);
38 if(pBuf == NULL)
39 {
40 perror("malloc");
41 return -1;
42 }
43
44 /*
45 attr.mq_flags = 0; //取消非阻塞
46 if(mq_setattr(mqd, &attr, NULL) == -1)
47 {
48 perror("mq_setattr");
49 return -1;
50 }
51 */
52 int i = 0;
53 while(1)
54 {
55 sprintf(pBuf, "Data%d", i++);
56 int ret = mq_send(mqd, pBuf, strlen(pBuf) + 1, 10);
57 if(ret == -1)
58 {
59 if(errno == EAGAIN)
60 {
61 printf("no block, continue to write\n");
62 continue;
63 }
64 perror("mq_send");
65 return -1;
66 }
67 printf("send data : %s\n", pBuf);
68 sleep(1);
69 }
70
71 free(pBuf);
72 return 0;
73 }

进程间通信-POSIX 消息队列的更多相关文章

  1. 进程间通信--POSIX消息队列

    相关函数: mqd_t mq_open(const char *name, int oflag); mqd_t mq_send(mqd_t mqdes, const char *msg_ptr, si ...

  2. Linux 进程间通信(posix消息队列 简单)实例

    Linux 进程间通信(posix消息队列 简单)实例 详情见: http://www.linuxidc.com/Linux/2011-10/44828.htm 编译: gcc -o consumer ...

  3. Linux环境编程之IPC进程间通信(五):Posix消息队列1

    对于管道和FIFO来说.必须应该先有读取者存在.否则先有写入者是没有意义的. 而消息队列则不同,它是一个消息链表,有足够写权限的线程可往别的队列中放置消息,有足够读权限的线程可从队列中取走消息.每一个 ...

  4. Linux进程间通信(IPC)编程实践(十二)Posix消息队列--基本API的使用

    posix消息队列与system v消息队列的区别: (1)对posix消息队列的读总是返回最高优先级的最早消息,对system v消息队列的读则能够返回随意指定优先级的消息. (2)当往一个空队列放 ...

  5. 练习--LINUX进程间通信之消息队列MSG

    https://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 继续坚持,或许不能深刻理解,但至少要保证有印象. ~~~~~~~~~~~~~~ 消息队 ...

  6. Posix消息队列实现机制

    本文是对<Unix 网络编程 卷2:进程通信>的笔记. 引言 消息队列是进程间通信的一种方式,可是如果不理解他的实现原理,会有众多不理解之处,下面就结合本书中的例子,对posix消息队列来 ...

  7. UNIX IPC: POSIX 消息队列 与 信号

    POSIX消息队列可以注册空队列有消息到达时所触发的信号,而信号触发对应的信号处理函数. 下面是一份基本的消息队列和信号处理结合的代码(修改自UNIX网络编程:进程间通信) #include < ...

  8. [转]Linux进程通信之POSIX消息队列

    进程间的消息队列可以用这个实现,学习了下. http://blog.csdn.net/anonymalias/article/details/9799645?utm_source=tuicool&am ...

  9. Linux IPC POSIX 消息队列

    模型: #include<mqueue.h> #include <sys/stat.h> #include <fcntl.h> mq_open() //创建/获取消 ...

  10. Posix消息队列

    转载于:http://blog.csdn.net/zx714311728/article/details/53197196 1.消息队列 消息队列可以认为是一个消息链表,消息队列是随内核持续的.队列中 ...

随机推荐

  1. 互联网和DeepSeak时代,获取信息这么容易,为什么我们还是学习不好?

    因为人性倾向于立即获得享受,而不是延迟获得享受,然而,学习就是延迟获得享受,所以,学习,其实是反人性的一种活动. 学习,特别是对知识的深入学习,其实需要付出大量的时间和精力,这个过程中必然伴随着各种各 ...

  2. 全程使用 AI 从 0 到 1 写了个小工具

    背景 好长时间没写技术方面的文章了,主要的原因是AI的发展实在太快太快,尤其是从去年ChatGPT的普及到今年DeepSeek的爆火,AI的世界可谓是三天一个小变化五天一个大版本,AI的能力每天都在以 ...

  3. JS经纬度坐标转换

    var GPS = { PI : 3.14159265358979324, x_pi : 3.14159265358979324 * 3000.0 / 180.0, delta : function ...

  4. script crossorigin 属性

    来源:https://juejin.cn/post/6969825311361859598 <script src="xxxx" crossorigin="anon ...

  5. 【Abaqus】Composite Layup建模

    abaqus 的3个复合材料建模途径: 传统的material->section->orientation->step->job的建模方式 Composite Layup建模方 ...

  6. Golang 入门 : Go语言介绍

    简介 Go 语言又称 Golang,由 Google 公司于 2009 年发布,近几年伴随着云计算.微服务.分布式的发展而迅速崛起,跻身主流编程语言之列,和 Java 类似,它是一门静态的.强类型的. ...

  7. Go语言之sync包 WaitGroup的使用

    WaitGroup 是什么以及它能为我们解决什么问题? WaitGroup在go语言中,用于线程同步,单从字面意思理解,wait等待的意思,group组.团队的意思,WaitGroup就是指等待一组, ...

  8. 基于SLAM系统建图仿真,完成定位仿真

    博客地址:https://www.cnblogs.com/zylyehuo/ 基于SLAM系统完成建图仿真,详见之前的博客 基于Gazebo搭建移动机器人,并结合SLAM系统完成建图仿真 - zyly ...

  9. 面试题-Netty框架

    前言 Netty框架部分的题目,是我根据Java Guide的面试突击版本V3.0再整理出来的,其中,我选择了一些比较重要的问题,并重新做出相应回答,并添加了一些比较重要的问题,希望对大家起到一定的帮 ...

  10. deepseek+dify工作流实现代码审计

    一.登录dify,设置deepseek apikey 登录dify(https://cloud.dify.ai/signin) 设置deepseek apikey 二.创建dify工作流 添加工作流应 ...