• Linux进程通信——消息队列


    概念

    消息队列,是消息的链接表,存放在内核中。一个消息队列由一个标识符(即队列ID)来标识。

    特点

    1.消息队列是面向记录的,其中的消息具有特定的格式以及特定的优先级。(消息队列是结构体)
    2.消息队列独立于发送与接收进程。进程终止时,消息队列及其内容并不会被删除
    3.消息队列可以实现消息的随机查询,消息不一定要以先进先出的次席读取,也可以按消息的类型读取

    两者的队列ID需相同才能成功实现存放数据和取数据,如图都指向队列1的最后一个。

    消息队列与管道的不同点:写入读取后内容还存在于Linux内核中,不会跟管道一样读取完就消失。

    创建

    从消息队列特点可知,两个进程分别需要同队列ID相同的队列进行写入数据并读取数据,此时要想成功创建一个消息队列,需关心两个问题:

    问题一:进程B如何添加消息到队列

    问题二:进程A如何读取队列的消息

    头文件

    #include 

    常用API

    msgget()

    创建或打开消息队列:成功返回队列ID,失败返回-1

    int msgget(key_t key, int flag);
    key是一个索引值,为非负数,将通过索引值在Linux内核找到队列
    flag打开队列的方式

    在以下两种情况下,msgget将创建一个新的消息队列:
    1、如果没有与键值key相对应的消息队列,并且flag中包含了IPC_CREAT标志位。

    msgget(key,IPC_CREAT);

    2、key参数为IPC_PRIVATE

    msgget(key,IPC_PRIVATE);
    

    msgsnd()

    添加消息:成功返回0,失败返回-1

    int msgsnd(int msqid, const void *ptr, size_t size, int flag);
    

    msqid:消息队列的ID
    ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

    1. struct msgbuf
    2. {
    3.     long mtype; //消息类型,必须大于0
    4.     char mtext[1];//消息文本
    5. };

    size:数据的长度
    flag:0,表示忽略,表示进程将被阻塞直到函数可以从队列中得到符合条件的消息为止;(还有许多,此处省略)

    msgrcv()

    读取消息:成功返回消息数据的长度,失败返回-1

    int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
    

    msqid:消息队列的ID
    ptr:写入的数据,指向消息缓冲区的指针,此位置用来暂时存储发送和接收的消息,是一个用户可定义的通用结构,形态如下:

    1. struct msgbuf
    2. {
    3.     long mtype; //消息类型,必须大于0
    4.     char mtext[1];//消息文本
    5. };

    type:消息类型

    type = 0返回队列中的第一个消息
    type >0返回队列中消息类型为 type 的第一个消息
    type< 0返回队列中消息类型值小于或等于 type 绝对值的消息,如果有多个,则取类型值最小的消息

    可以看出,type值非 0 时用于以非先进先出次序读消息。也可以把 type 看做优先级的权值。

    size:数据的长度

    flag:0,表示忽略,表示进程将被阻塞直到函数可以从队列中得到符合条件的消息为止;(还有许多,此处省略)

    代码展示

    get.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. struct msgbuf
    6. {
    7. long mtype;
    8. char mtext[128];
    9. };
    10. int main()
    11. {
    12. int msgId;//创建消息队列ID
    13. struct msgbuf readBuf;//定义一个读取数据的结构体
    14. msgId = msgget(1234,IPC_CREAT|0777);//在内核中打开或建立键值为1234的,权限为0777的消息队列
    15. if(msgId == -1)//如果创建失败则执行下面代码
    16. {
    17. printf("create queue failed\n");
    18. }
    19. msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),888,0);//从队列中获取888类型的数据并存放到结构体的mtext中,如果队列中未出现888类型的数据,则程序阻塞在这里,这里的888需要与写入队列类型数据一致
    20. printf("read from queue:%s\n",readBuf.mtext);
    21. struct msgbuf sendBuf = {999,"thank you for reach\n"};//读取完毕后将字符串内容写入到999类型的数据中,这里的999类型需要与读取的类型数据一致
    22. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//将上一行的结构体数据写入1234消息队列中
    23. return 0;
    24. }

    send.c

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. struct msgbuf
    7. {
    8. long mtype;
    9. char mtext[128];
    10. };
    11. int main()
    12. {
    13. int msgId;
    14. struct msgbuf sendBuf = {888,"this is message from queue\n"};//将字符串内容写入到888类型的数据中,这里的888类型需要与读取的类型数据一致
    15. struct msgbuf readBuf;
    16. msgId = msgget(1234,IPC_CREAT|0777);
    17. if(msgId == -1)
    18. {
    19. printf("create queue failed\n");
    20. }
    21. msgsnd(msgId,&sendBuf,strlen(sendBuf.mtext),0);//将结构体内容写入到1234消息队列中
    22. msgrcv(msgId,&readBuf,sizeof(readBuf.mtext),999,0);//写入之后从队列中获取999类型的数据并存放到结构体的mtext中,如果队列中未出现999类型的数据,则程序阻塞在这里,这里的999需要与写入队列类型数据一致
    23. printf("return form queue:%s\n",readBuf.mtext);
    24. return 0;
    25. }

    运行get.c,创建并打开键值为1234的消息队列,但此时表现为堵塞状态,因为队列里没有888类型的数据

    运行send.c,创建并打开键值为1234的消息队列,往队列里写入888类型的数据,此时接收端会接受到写入端写入消息队列的数据并将其读取,同时让接收端往队列里写入999类型的数据,让写入段接受999类型的数据并读取

  • 相关阅读:
    Golang Break、Continue跳出多层循环
    Pymoo学习 (1):基本概念
    图文看懂JavaScritpt引擎V8与JS执行过程
    Java21 LTS版本
    从0到1学SpringCloud——12 gateway 动态配置网关路由规则
    深度监督(中继监督)
    Mybatis的简单的配置详解
    LeeCode热题100(两数之和)
    华为机试 - 等和子数组最小和
    CVE-2022-22954-VMware Workspace ONE Access SSTI远程代码执行流量特征
  • 原文地址:https://blog.csdn.net/2301_78772787/article/details/134535601