当前位置: 首页 > backend >正文

嵌入式学习笔记--Linux系统编程阶段--DAY06进程间通信-消息队列

1.消息队列

主要应用场景是多对多的进程间通信。

   “消息”是在两台计算机间传送的数据单位。消息可以非常简单,例如只包含文本字符串;也可以更复杂,可能包含嵌入对象。

    消息被发送到队列中。“消息队列”是在消息的传输过程中保存消息的容器。消息队列管理器在将消息从它的源中继到它的目标时充当中间人。队列的主要目的是提供路由并保证消息的传递;如果发送消息时接收者不可用,消息队列会保留消息,直到可以成功地传递它。

     消息队列就是一个消息的链表(链表的每一个节点就是一个消息队列)。可以把消息看作一个记录,具有特定的格式以及特定的优先级。对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息;对消息队列有读权限的进程则可以从消息队列中读走消息。消息队列是随内核持续的

个人理解:可以将消息队列理解为一个快递柜,送快递的将快递放入快递柜,取快递的去取。

消息队列的特点:

1、消息队列中的消息是有类型的(设置flag,只能让固定的人去接收)。
2、消息队列中的消息是有格式的。(管道数据无格式)
3、消息队列可以实现消息的随机查询。消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取
4、消息队列允许一个或多个进程向它写入或者读取消息。
5、与无名管道、命名管道一样,从消息队列中读出消息,消息队列中对应的数据都会被删除
6、每个消息队列都有消息队列标识符,消息队列的标识符在整个系统中是唯一 的。
7、只有内核重启或人工删除消息队列时,该消息队列才会被删除。若不人工删除消息队列,消息队列会一直存在于系统中。

在 ubuntu 某些版本中消息队列限制值如下:

Ø每个消息内容最多为 8K 字节

Ø每个消息队列容量最多为 16K 字节

Ø系统中消息队列个数最多为 1609 个

Ø系统中消息个数最多为 16384 个

2.消息队列的API

要想创建消息队列,先拿到那个唯一的标识符(key值)。

2.1获取唯一的key值

ftok函数:

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
获得项目相关的唯一的 IPC 键值。
参数:
pathname路径名,一定是绝对路径,因为后续进行通信的进程不在一个文件路径下
proj_id:项目 ID,非 0 整数(只有低 8 位有效)
返回值:
成功返回 key 值,失败返回 -1
key_t key = ftok("/home/qf/桌面/Linux编程",2504);

2.2根据key值创建唯一的消息队列

msgget函数:

#include <sys/msg.h>
int msgget(key_t key, int msgflg);

函数功能:创建一个新的或打开一个已经存在的消息队列。不同的进程调用此函数,只要用相同的 key 值就能得到同一个消息队列的标识符。
参数:
key:IPC 键值。
msgflg:标识函数的行为及消息队列的权限。
参数:
msgflg 的取值:
        IPC_CREAT:创建消息队列。
        IPC_EXCL:检测消息队列是否存在。
位或权限位:消息队列位或权限位后可以设置消息队列的访问权限,格式和 open 函数的
mode_t 一样,但可执行权限未使用
返回值:
成功:消息队列的标识符,失败:返回-1。

代码案例:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_CREAT |0666);printf("msgid = %d\n",msgid);return 0;
}

shell 命令 ipcs -q查看消息队列

删除消息队列

ipcrm -q msqid(消息队列的id)

3.消息队列的格式

本质上是一个结构体:

这个消息队列需要自己定义。

typedef struct _msg
{
long mtype; /*消息类型*/   必须是第一个成员,剩下的随意,顺序,大小都无所谓
char mtext[100]; /*消息正文*/
... /*消息的正文可以有多个成员*/
}MSG;
消息类型必须是长整型的,而且必须是结构体类 型的第一个成员,类型下面是 消息正文,正文可以 有多个成员(正文成员可以是任意数据类型的)。

3.1发送信息

msgsnd函数:

#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp,size_t msgsz, int msgflg);
功能:
        将新消息添加到消息队列。
参数:
        msgid:消息队列的标识符。
        msgp:待发送消息结构体的地址。
        msgsz:消息正文的字节数。(除去消息队列类型)
        msgflg:函数的控制属性
                0msgsnd 调用阻塞直到条件满足为止。(推荐)
                IPC_NOWAIT: 若消息没有立即发送则调用该函数的进程会立即返回。
返回值:
        成功:0;失败:返回-1
发送代码案例:给消息类型10发送hello world字符串
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/  
char mtext[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_CREAT |0666);printf("msgid = %d\n",msgid);MSG msg;msg.mtype =10;  //消息类型  后续收消息类型为10的进程可以收到此消息strcpy(msg.mtext,"hello world!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);return 0;
}

3.2接收消息

msgrcv函数:
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
功能:
        从标识符为 msqid 的消息队列中接收一个消息。一旦接收消息成功,则消息 在消息队列中被删除。
参数:
        msqid:消息队列的标识符,代表要从哪个消息列中获取消息。
        msgp: 存放消息结构体的地址。
        msgsz:消息正文的字节数。
        msgtyp:消息的类型、可以有以下几种类型
                msgtyp = 0:返回队列中的第一个消息(所以结构体内的type不能是0)
                msgtyp > 0:返回队列中消息类型为 msgtyp 的消息
                msgtyp < 0:返回队列中消息类型值小于或等于 msgtyp 绝对值的消息,如果
种消息有若干个,则取类型值 最小的消息

                注意:若消息队列中有多种类型的消息,msgrcv 获取消息的时候按消息类型获取,不是 先进先出的。 在获取某类型消息的时候,若队列中有多条此类型的消息,则获取最先添加的消 息,即先进先出原则。

         msgflg:函数的控制属性
                    0msgrcv 调用阻塞直到接收消息成功为止。
                    MSG_NOERROR:若返回的消息字节数比 nbytes 字节数多,则消息就会截短到 nbytes 字节, 且不通知消息发送进程。
                    IPC_NOWAIT:调用进程会立即返回。若没有收到消息则立即返回-1
返回值:
成功返回读取消息的长度,失败返回-1
接收代码案例:
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/  
char mtext[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =10;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("%s\n",msg.mtext);return 0;
}

4.消息队列案例

lucy   bobo   tom互发消息

lucy负责发消息

bobo负责接收消息类型为10的消息

tom负责接收消息类型为20的消息

lucy部分:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/  
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);int msgid = msgget(key,IPC_CREAT |0666);MSG msg;msg.mtype =10;strcpy(msg.name,"lucy!");strcpy(msg.data,"i am lucy!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);msg.mtype =20;strcpy(msg.name,"lucy!");strcpy(msg.data,"i am lucy!");msgsnd(msgid,&msg,sizeof(msg)-sizeof(long),0);return 0;}

bobo部分:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/  
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =10;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("收了%s的消息\n",msg.name);printf("收到的消息内容是:%s\n",msg.data);return 0;
}

tom部分:

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include<string.h>typedef struct _msg
{
long mtype; /*消息类型*/  
char name[16];
char data[100]; /*消息正文*/
}MSG;int main(int argc, char const *argv[])
{key_t key = ftok("/home/qf/桌面/Linux编程",2504);printf("%d\n",key);int msgid = msgget(key,IPC_EXCL |0666);MSG msg;msg.mtype =20;msgrcv(msgid,&msg,sizeof(msg)-sizeof(long),msg.mtype,0);printf("收了%s的消息\n",msg.name);printf("收到的消息内容是:%s\n",msg.data);return 0;
}

5.消息队列的控制

我们不可能每次删除消息队列都用命令删除,也可以在代码中

msgctl函数:
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:
        对消息队列进行各种控制,如修改消息队列的属性,或删除消息消息队列。
参数:
        msqid:消息队列的标识符。
        cmd:函数功能的控制。
        buf:msqid_ds 数据类型的地址,用来存放或更改消息队列的属性。
                cmd:函数功能的控制
                        IPC_RMID:删除由 msqid 指示的消息队列,将它从系统中删除并破坏相关
数据结构。(所以后续就可以传NULL了)(用的最多)
                        IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值存入到由 buf指向的结构中。
                        IPC_SET:将 msqid 相关的数据结构中的元素设置为由 buf 指向的结构中
的对应值。
返回值:成功:返回 0;失败:返回 -1
其中struct msqid_ds结构体成员:
struct msqid_ds {struct ipc_perm msg_perm;     /* Ownership and permissions */time_t          msg_stime;    /* Time of last msgsnd(2) */time_t          msg_rtime;    /* Time of last msgrcv(2) */time_t          msg_ctime;    /* Time of last change */unsigned long   __msg_cbytes; /* Current number of bytes inqueue (nonstandard) */msgqnum_t       msg_qnum;     /* Current number of messagesin queue */msglen_t        msg_qbytes;   /* Maximum number of bytesallowed in queue */pid_t           msg_lspid;    /* PID of last msgsnd(2) */pid_t           msg_lrpid;    /* PID of last msgrcv(2) */};

struct ipc_perm 结构体类型:

struct ipc_perm {key_t          __key;       /* Key supplied to msgget(2) */uid_t          uid;         /* Effective UID of owner */gid_t          gid;         /* Effective GID of owner */uid_t          cuid;        /* Effective UID of creator */gid_t          cgid;        /* Effective GID of creator */unsigned short mode;        /* Permissions */unsigned short __seq;       /* Sequence number */};

6.消息队列的作业(多人聊天程序)

建议:
每个程序有两个任务,一个任务负责发消息,一个任务负责收消息。通过fork函数创建子进程实施多任务。
一个进程负责接收消息,只收某种类型的消息,只要别的进程发送此类型的消息,此进程就能收到。通过name成员可以知道是谁发的。
另一个进程负责发消息,可以通过输入来决定发送消息的类型。
设计程序的时候,接受消息的进程接收消息的类型不一样,这样就能实现发送的消息只能被特定的人收到,其他人收不到。
http://www.xdnf.cn/news/20367.html

相关文章:

  • Linux知识回顾总结----文件系统
  • 南科大适应、协同与规划的完美融合!P³:迈向多功能的具身智能体
  • 【基础-单选】下面哪一个事件方法可以获取到List滑动的偏移量
  • Flicking单图轮播无法拖动的问题
  • c++primer 个人学习总结-模板和泛型编程
  • 《QDebug 2025年8月》
  • 前端开发学习路径
  • LeetCode 468. 验证IP地址 - 详细解析
  • 嵌入式学习笔记--Linux系统编程阶段--DAY07进程间通信--存储映射和共享内存
  • 区块链技术
  • 如何减少微型导轨表面破损情况?
  • JWT概念及使用详解
  • Dart语言基础 关键字 var与dynamic
  • 整车无线布置的综述
  • 【完整源码+数据集+部署教程】室内场景分割系统源码和数据集:改进yolo11-DWR
  • 算法题(200):最大子段和(动态规划)
  • 责任链框架 03:处理器实现
  • 《Science》神经炎症综述思路套用:从机制到跨领域研究范式
  • Python实现生成矩形框、三角形框、六边形框和圆环点云
  • 自动拆箱和装箱的原理与作用
  • HMI(人机界面)
  • 【基础-单选】UIAbility实例创建完成时触发的回调
  • HTML 列表类型
  • 5-8单元格区域与VS数组应用(实例:提取满足条件的数据)
  • Qt多线程编程学习
  • EG2103 SOP-8 内置600V功率MOS管 栅极驱动芯片
  • I/O 多路复用 (I/O Multiplexing)
  • 四个关于云属性的四个卫星数据集的介绍
  • 基于Spring Boot + Vue3的办公用品申领管理系统
  • 部署AIRI