首页 > 系统相关 >进程间通信——消息队列(通俗易懂)

进程间通信——消息队列(通俗易懂)

时间:2024-09-03 20:25:12浏览次数:14  
标签:队列 通俗易懂 间通信 int 消息 msg include id

消息队列

概念

  • 消息队列是消息的链表,存放在内核中并由消息队列标识符标识,消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺陷。消息队列包括 POSIX 消息队列和 System V 消息队列。

  • 消息队列是 UNIX 下不同进程之间实现共享资源的一种机制,UNIX 允许不同进程将格式化的数据流以消息队列形式发送给任意进程,有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。

原理

image-20240903085842708

步骤

  1. 创建或者获取消息队列
  2. 读取或写入数据
  3. 删除消息队列(非必须)

创建/获取消息队列

功能

  • 创建或者获取消息队列

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgget(key_t key, int msgflg);
  • keyIPC_PRIVATE 或 通过ftok()获取
  • msgflg
    • IPC_CREAT | 0666,内核中不存在指定队列就创建,否则就获取它
    • IPC_CREAT | IPC_EXCL | 0666,若队列已存在则出错(函数返回-1)

返回值

  • 成功返回 msgid(队列ID)
  • 失败返回-1,并设置errnor

使用命令获取消息队列

ipcs -q # interprocess communication status——进程间通信的状态 q——queue  

读取/写入数据

写入数据

功能

  • 往消息队列中写入数据

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
  • msqidmsgget()的返回值(代表消息队列)

  • msgp:万能指针(写入的类型 + 数据)

    struct msgbuf {
    	long mtype;			// 消息类型(>0)
    	// 消息正文,多少字节随你而定
       	// ...
    };
    
    • 只要保证首4字节是一个整数(代表类型)就行(64位系统,建议用 long 类型)

    • 正文部分是什么数据类型都没关系,因为消息队列传递的是 2 进制数据

    • 示例:

      struct msgbuf {
      	long type;
      	char name[20];
      	int age;
      } msg;
      	
      struct msgbuf {
      	long type;
      	int start;
      	int end;
      } msg;
      
  • msgsz:数据域的字节数(不包含消息类型的大小)

    sizeof(struct msgbuf) - sizeof(long);
    
  • msgflg:0 阻塞 IPC_NOWAIT 不阻塞

消息队列的容量(msgp)

  • 消息的大小有一个最大限制,其定义在Linux/msg.h文件中

    #define MSGMAX 8192
    
  • 消息结构的总大小不能超过8192字节(包括type的大小)

返回值

  • 成功返回0
  • 失败返回-1,并设置 errno

读取数据

功能

  • 从消息队列中读取数据

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
  • msqid:信息队列的ID号

  • msgp:用来保存读取的信息

    struct msgbuf {
    	long mtype;			// 消息类型(>0)
    	// 消息正文
       	// ...
    };
    
  • msgsz:数据域的字节数

    sizeof(struct msgbuf) - sizeof(long);
    
  • msgtyp:读取的数据 在消息队列中存储的 类型

    • >0:得到该种类型的第一个数据
    • ==0:得到整个消息队列的第一个数据
    • <0:得到 小于或等于$|msgtyp|$类型的 最小类型的 第一个数据
  • msgflg:0 阻塞 IPC_NOWAIT 不阻塞

返回值

  • 成功返回 数据区的字节数
  • 失败返回-1,并设置 errno

删除消息队列

功能

  • 控制 消息队列

函数原型

#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
  • msqid:消息队列的ID号
  • cmd
    • IPC_RMID:删除消息队列,第三个参数为NULL

返回值

  • IPC_RMID 成功返回0,失败返回-1,并设置 errno

实现双向通信

方法一:创建两个消息队列来进行通信

image-20240903141847852

  • 不同颜色,代表不同的消息队列

two_msg_way1_1.c

  • 父进程中,使用通过 key1 建立的消息队列1,发送信息(数据类型为1)
  • 子进程中,使用通过 key2 建立的消息队列2,接受信息(数据类型为2)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>

// 消息的缓冲区结构
struct msgmbuf
{
    long mtype;       // 消息类型
    char mtext[10];   // 消息内容
};

int main(void)
{
    int            ret = -1;
    key_t          key1, key2;
    int            smsg_id, rmsg_id;        // 收消息与发消息的队列id
    struct msgmbuf msg_mbuf;                // 创建消息缓冲区
    char*          msgpath1 = "/usr/bin";   // 消息key1产生所用的路径
    char*          msgpath2 = "/usr/bin";   // 消息key2产生所用的路径
    pid_t          pid;

    // 获得 key1 和 key2
    key1 = ftok(msgpath1, 'b');
    key2 = ftok(msgpath2, 'a');
    if (key1 != -1 && key2 != -1)
    {
        printf("成功建立KEY\n");
    }
    else
    {
        perror("建立KEY失败");
        exit(-1);
    }

    smsg_id = msgget(key1, IPC_CREAT | 0666);   // 使用 key1 建立发消息的消息队列
    rmsg_id = msgget(key2, IPC_CREAT | 0666);   // 使用 key2 建立收消息的消息队列
    if (-1 == smsg_id || -1 == rmsg_id)
    {
        perror("msgget error");
        exit(-1);
    }

    // 通过fork()创建子进程,主进程进行发消息,子进程进行收消息
    pid = fork();
    while (1)
    {
        if (pid > 0)   // 主进程
        {
            msg_mbuf.mtype = 1;                           // 设置发送的消息类型
            scanf("%s", msg_mbuf.mtext);                  // 用户输入内容
            if (strncmp(msg_mbuf.mtext, "end", 3) == 0)   // 如果前三个字符为end,则跳出循环
            {
                break;
            }
            ret = msgsnd(smsg_id, &msg_mbuf, sizeof(struct msgmbuf) - sizeof(msg_mbuf.mtype),
                         0);   // 发送消息
            if (ret == -1)
            {
                perror("msgsnd error");
                exit(-1);
            }
        }
        else   // 子进程
        {
            ret = msgrcv(rmsg_id, &msg_mbuf, sizeof(struct msgmbuf) - sizeof(msg_mbuf.mtype), 2,
                         0);   //接收消息
            if (-1 == ret)
            {
                perror("msgrcv error");
                exit(-1);
            }
            else
            {
                printf("接收消息成功,长度:%d\n", ret);
                printf("content:%s\n\n", msg_mbuf.mtext);
            }
        }
    }

    // 注意:两个程序删除的消息队列
    ret = msgctl(smsg_id, IPC_RMID, NULL);   // 删除发消息的队列
    if (-1 == ret)
    {
        perror("msgctl error");
        exit(-1);
    }
    return 0;
}

two_msg_way1_2.c

  • 父进程中,使用通过 key2 建立的消息队列2,发送信息(数据类型为2)
  • 子进程中,使用通过 key1 建立的消息队列1,接收信息(数据类型为1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <sys/types.h>
#include <unistd.h>

// 消息的缓冲区结构
struct msgmbuf
{
    long mtype;       // 消息类型
    char mtext[10];   // 消息内容
};

int main(void)
{
    int            ret = -1;
    key_t          key1, key2;
    int            smsg_id, rmsg_id;        // 收消息与发消息的队列id
    struct msgmbuf msg_mbuf;                // 创建消息缓冲区
    char*          msgpath1 = "/usr/bin";   // 消息key1产生所用的路径
    char*          msgpath2 = "/usr/bin";   // 消息key2产生所用的路径
    pid_t          pid;

    // 获得 key1 和 key2
    key1 = ftok(msgpath1, 'b');
    key2 = ftok(msgpath2, 'a');
    if (key1 != -1 && key2 != -1)
    {
        printf("成功建立KEY\n");
    }
    else
    {
        perror("建立KEY失败");
        exit(-1);
    }

    smsg_id = msgget(key2, IPC_CREAT | 0666);   // 使用 key2 建立收消息的消息队列
    rmsg_id = msgget(key1, IPC_CREAT | 0666);   // 使用 key1 建立发消息的消息队列
    if (-1 == smsg_id || -1 == rmsg_id)
    {
        perror("msgget error");
        exit(-1);
    }

    // 通过fork()创建子进程,主进程进行发消息,子进程进行收消息
    pid = fork();
    while (1)
    {
        if (pid > 0)   // 主进程
        {
            msg_mbuf.mtype = 2;                           // 设置发送的消息类型
            scanf("%s", msg_mbuf.mtext);                  // 用户输入内容
            if (strncmp(msg_mbuf.mtext, "end", 3) == 0)   // 如果前三个字符为end,则跳出循环
            {
                break;
            }
            ret = msgsnd(smsg_id, &msg_mbuf, sizeof(struct msgmbuf) - sizeof(msg_mbuf.mtype),
                         0);   // 发送消息
            if (ret == -1)
            {
                perror("msgsnd error");
                exit(-1);
            }
        }
        else   // 子进程
        {
            ret = msgrcv(rmsg_id, &msg_mbuf, sizeof(struct msgmbuf) - sizeof(msg_mbuf.mtype), 1,
                         0);   //接收消息
            if (-1 == ret)
            {
                perror("msgrcv error");
                exit(-1);
            }
            else
            {
                printf("接收消息成功,长度:%d\n", ret);
                printf("content:%s\n\n", msg_mbuf.mtext);
            }
        }
    }

    ret = msgctl(smsg_id, IPC_RMID, NULL);   // 删除发消息的队列
    if (-1 == ret)
    {
        perror("msgctl error");
        exit(-1);
    }
    return 0;
}

方法二:通过创建不同的消息类型来进行双向通信

  • 在同一个消息队列中,使用不同的消息类型来标识收发信息

image-20240903190216427

  • 图中不同的颜色,表示不同的消息类型

标签:队列,通俗易懂,间通信,int,消息,msg,include,id
From: https://www.cnblogs.com/General-xd/p/18395398

相关文章

  • 进程间通信(信号灯集、消息队列)
    1.信号灯集线程:全局变量,同步通过信号量初始化:sem_init(&sem,0,0);申请资源:sem_wait(&sem);P操作,-1释放资源:sem_post(&sem);V操作,+11.1特点信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;而Posi......
  • 进程间通信----信号灯集
    目录一丶概念二丶操作步骤三丶命令四丶函数接口1.创建信号灯集2.初始化或删除信号灯集3.pv操作练习:一丶概念        信号灯(semaphore),也叫信号量。它是不同进程间或一个给定进程内部不同线程间同步的机制;        SystemV信号灯集是一个或......
  • 优先队列模板
    基础用法intmain(){ /* c++优先队列默认为大根堆 */ priority_queue<int,vector<int>>heap; heap.push(1); heap.push(2); heap.push(3); while(heap.size()){ cout<<heap.top()<<''; heap.pop(); } /*output:321*/ /* 优先队......
  • Spring中基于redis stream 的消息队列实现方法
       本文主要介绍了消息队列的概念性质和应用场景,介绍了kafka、rabbitMq常用消息队列中间件的应用模型及消息队列的实现方式,并实战了在Spring中基于redisstream的消息队列实现方法。一、消息队列   消息队列是一种进程间通信或者同一个进程中不同线程间的通信方......
  • [数据结构] 循环队列
    front:头指针rear:尾指针maxsize:数组长度循环队列通常会让留空数组中的一位,区分队列为空和队列为满的状态。入队移动rear,出队移动front。形式1(默认):front指向队头元素的前一位,而rear指向队尾元素。队列为空:front==rear队列为满:front==(rear+1)%maxsize元素个数:(r......
  • linux进程间通信——信号量(通俗易懂,看这一篇就够了)
    信号量概念特点信号量实际是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。很多进程会访问同一资源,或者向共享内存写入一些东西,为防止争夺资源混乱。可以给一些进程上锁,让其排队等待工作原理P(sv):如果sv的值大于零,就给它减1;如果它的值为......
  • IO进程day07(信号灯集、消息队列)
    【1】信号灯集semaphore1》概念信号灯(semaphore),也叫信号量,信号灯集是一个信号灯的集合。它是不同进程间或一个给定进程内部不同线程间同步的机制;而Posix信号灯指的是单个计数信号灯:无名信号灯、有名信号灯。(咱们学的是无名信号灯)SystemV的信号灯是一个或者多个信号......
  • 算法练习题09:滑动窗口最大值(队列、双端队列)
    classSolution{publicint[]maxSlidingWindow(int[]nums,intk){if(nums==null||nums.length==0){returnnewint[0];}intn=nums.length;int[]result=newint[n-k+1];Deque<Integer&......
  • 线性表之队列API设计思路
    Java学习手册+面试指南:https://javaxiaobear.cn队列是一种基于先进先出(FIFO)的数据结构,是一种只能在一端进行插入,在另一端进行删除操作的特殊线性表,它按照先进先出的原则存储数据,先进入的数据,在读取数据时先读被读出来。1、队列的API设计类名Queue构造方法Queue():创建Queue对象成......
  • Java 最小优先队列API设计与实现
    Java学习+面试指南:https://javaxiaobear.cn最小的元素放在数组的索引1处。每个结点的数据总是小于等于它的两个子结点的数据。1、API设计类名MinPriorityQueue构造方法MinPriorityQueue(intcapacity):创建容量为capacity的MinPriorityQueue对象成员方法privatebooleanless(inti......