14-3-进程间通信-消息队列

前面提到的管道pipe和fifo是半双工的,在某些场景不能发挥作用;

接下来描述的是消息队列(一种全双工的通信方式);

比如消息队列可以实现两个进程互发消息(不像管道,只能1个进程发消息,1个进程读消息);

注意:消息队列是由内核进行管理的,初级开发者可以先不了解其机制。

初级开发者应该关注:如何把消息加到队列?如何从队列获取消息。

一、消息队列的基本概念

1.特点

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

(1)消息队列是面向记录的,其中的消息具有特定的格式及特定的优先级;

(2)消息队列独立于发送和接收进程。进程终止时,消息队列及其内容并不会被删除;

(3)消息队列可以实现消息的随机查询;消息不一定要 以先进先出的次序读取,也可以按消息的类型读取;(初级开发者一般 采用按消息的类型读取数据)

2.API

头文件:#include<sys/msg.h>

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

int msgget(key_ t key, int msgflg); 
参数:

    key:消息队列的标识符
    msgflg:创建的标志,例如IPC_CREAT
    IPC_CREAT:如果不存在就创建:按位或上一个权限(8进制的数字)

返回值:

    成功:返回队列ID
    失败:返回-1,并设置erron

注意:以下两种勤情况,msgget将创建1个新的消息队列
    (1)如果没有与键值key相对应的消息队列,并且flag中 包含了IPC_CREAT标志位。
    (2)key参数为IPC_PRIVATE.
    

(2)添加消息(成功返回0,失败返回-1)

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
msgid:消息队列ID
msgp:指向msgbuf 的指针,用来指定发送的消息 
msgsz:要发送消息的长度,消息内容的长度
msgflg:创建标记,如果指定IPC_NOWAIT,失败会立即返回
        0:阻塞发送
        IPC_NOWAIT:非阻塞发送

返回值:
    成功:返回0
    失败:返回-1,并设置erron

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

 int msgrcv(int msqid, void *msgp, sizet msgsz, long msgtyp, int msgflg); 
msgid:消息队列ID
msgp:指向msgbuf的指针,用来接收消息
msgsz:要接收消息的长度
        注意:参数msgsz 指定由msgp 参数指向的结构的成员mtext的最大大小(以字节为单位),msgtyp 也有,3种方式:
msgtyp:接收消息的方式
        1. msgtyp = 0:读取队列中的第一条消息(不在乎当前对头元素时什么消息类型,将他当作普通队列来处理)
        2. msgtyp > 0:读取队列中类型为msgtyp 的第一条消息。(就是读取对列元素中第一个香蕉)除非在msgflg中指定了MSG_ EXCEPT, 将读取类型不等于msgtyp 绝对值的第一条消息
        3. msgtyp< : 0:读取队列中最小类型小于或等于msgtyp 绝对值的第一条消息
msgflg:创建标记,如果指定IPC_ NOWAIT,获取失败会立刻返回

返回值:
    成功返回实际读取消息的字节数,,
    失败返回-1,并设置erron

注意 :msgflg置为0时,是以默认的方式读数据,如果读不到数据会阻塞。

 (4)控制消息队列(成功返回0,失败返回-1)

int msgctl(int msqid, int cmd, struct msqid_ ds *buf);
参数:
msqid:消息队列ID
cmd:控制命令,
        例如IPC_ RMID,删除命令 ,
        IPC STAT,获取状态
buf:存储消息队列的相关信息的buf

返回值:
    成功根据不同的cmd有不同的返回值,
    失败返回-1,并设置erron

 

3.基本开发流程

.针对消息队列,
若A想对B进行通信(假设A从B读信息),
A需要做如下操作:
(1)获取队列
(2)A读B发送过来的消息
B要做的操作:
(1)创建队列
(2)B向A发送消息(即写数据放到队列中)

4.基于API的基本实验

实验要求:创建两个进程(进程A和进程B),两个进程相互发送和接收消息。

以下是进程A的步骤:

(1)创建/打开 消息队列(key=0x1234)

(2)将要发送的消息:

struct DuiLie_data{
        long type;
        char send_buff[128];
};

        struct DuiLie_data STU_sendbuff = {111,"hello\n"};

注意:STU_sendbuff 的type要和msgrcv()的倒数第二个参数一致;

消息队列独立于发送和接收进程。进程终止时,消息队列及其内容并不会被删除;

(3)创建消息队列,返回值为消息队列ID;msgID = msgget(0x1234,IPC_CREAT|0777);

        判断返回值msgID;

        返回值为-1,则表示队列 创建失败;

        否则创建成功;

(5)通过队列发送消息:

send_flag =msgsnd(msgID,&STU_sendbuff,strlen(STU_sendbuff.send_buff),0);

        判断返回值send_flag ;

        返回值为-1,表示发送失败;

        返回值为0,表示发送成功;

(6)通过队列读取消息:msgrcv(msgID,&STU_readbuff,sizeof(STU_readbuff),111,0);

参数1:消息队列的ID

参数2:一个指针,指向自定义的结构体,该结构体有2个成员(成员1:消息类型,成员2:消息内容)

参数3:参数2所指向的结构体的大小。

参数 4:

        等于0,读取队列中的第一条消息(不在乎当前对头元素时什么消息类型,将他当作普通队列来处理);

        大于0,读取队列中类型为msgtyp 的第一条消息。(就是读取对列元素中第一个消息)除非在msgflg中指定了MSG_ EXCEPT, 将读取类型不等于msgtyp 绝对值的第一条消息。

        小于0,读取队列中最小类型小于或等于msgtyp 绝对值的第一条消息

参数5:msgflg置为0时,是以默认的方式读数据,如果读不到数据会阻塞。

------

进程B的步骤与进程 A 的步骤基本一致,不再赘述。

main.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct DuiLie_data{
        long type;
        char send_buff[128];
};

int main()
{
        int msgID;
        char send_flag;
//      char send_buff[128] = "12345\n";
        struct DuiLie_data STU_readbuff;
        struct DuiLie_data STU_sendbuff = {111,"hello\n"};

        msgID = msgget(0x1223,IPC_CREAT|0777);

        if( msgID == 0)
        {
                printf("creat failed\n");
        }
        else
        {
                printf("duilie creat success! msgID = %x\n",msgID);
                send_flag = msgsnd(msgID,&STU_sendbuff,strlen(STU_sendbuff.send_buff),0);

                if(send_flag == 0)
                {
                        printf("send success\n");
                }
                else if(send_flag == -1)
                {
                        printf("send failed\n");
                }
                printf("A will get data from queue\n");
                msgrcv(msgID,&STU_readbuff,sizeof(STU_readbuff),111,0);
                printf("return from get B:%s\n",STU_readbuff.send_buff);

        }
        return 0;
}

send.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
#include <string.h>
struct DuiLie_data{
        long type;
        char send_buff[128];
};

int main()
{
        int msgID;
        char send_flag;
        struct DuiLie_data STU1 = {111,"123456789\n"};
        //char read_buff[128];
        struct DuiLie_data readSTU ;

        msgID = msgget(0x1223,IPC_CREAT|0777);

        if( msgID == -1)
        {
                printf("creat failed\n");
        }
        else
        {
                printf("duilie creat success! msgID = %x\n",msgID);
                printf("B will get data from queue\n");
                msgrcv(msgID,&readSTU,sizeof(readSTU.send_buff),111,0);
                printf("return from get A:%s\n",readSTU.send_buff);

                send_flag = msgsnd(msgID,&STU1,sizeof(STU1),0);
                if(send_flag == 0)
                {
                        printf("send success\n");
                }
                else if(send_flag == -1)
                {
                        printf("send failed\n");
                }


        }
        return 0;
}

运行结果如图所示:

 

 5.补充的知识点

(1)键值的生成及消息队列的移除

系统IPC键值的格式转换函数:ftok;

建立IPC通信(消息队列,信号量,共享内存)时,必须指定1个ID值。通常情况下,该ID通过ftok函数得到;
 

头文件:#include <sys/types.h>

        #include <sys//ipc.h>


函数原型:key_t ftok(const char *fname,int id)

fname :是开发者指定的文件名(已存在的文件名),一般使用当前目录;


如:
ket_t key;

key = ftok(".",1);//这样就是把当前目录设置为fname;

id是子序号,虽然是整型,但只能 使用8bits(1~255)

 

在一般的UNIX实现中,是将文件的索引节点号取出,前面加上子序号得到key_t的返回值。

如指定文件的索引节点号为65538,换算成16进制为0x010002,而你指定的ID值为38,换算成16进制为0x26,则最后的key_t返回值为0x26010002。

 可以使用 ls-ai查看 当前目录的索引节点;

如图当前 目录的索引节点为1179979

 修改前面的实验;

把 msgID = msgget(0x1223,IPC_CREAT|0777);//手动生成键值

改为用ftok生成键值

    key_t key;
    key = ftok(".",'z');
    msgID = msgget(key,IPC_CREAT|0777);
    printf("key :%x\n",key);

运行结果如下:

自动生成的键值为7a01014b

 

 (2)msgctl移除队列

msgctl(msgID,IPC_RMID,NULL);//实现移除队列

两个文件都添加这句话,实现移除队列

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/16644.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

kali: kali工具-Ettercap

kali工具-Ettercap ettercap工具&#xff1a; 用来进行arp欺骗&#xff0c;可以进行ARP poisoning&#xff08;arp投毒&#xff09;&#xff0c;除此之外还可以其他功能&#xff1a; ettercap工具的arp投毒可以截取web服务器、FTP服务器账号密码等信息&#xff0c;简略后打印出…

前端学习之使用JavaScript

前情回顾&#xff1a;网页布局 JavaScript 简介 avaScript诞生于1995年&#xff0c;它的出现主要是用于处理网页中的前端验证。所谓的前端验证&#xff0c;就是指检查用户输入的内容是否符合一定的规则。比如&#xff1a;用户名的长度&#xff0c;密码的长度&#xff0c;邮箱的…

SQL中去除重复数据的几种方法,我一次性都告你​

使用SQL对数据进行提取和分析时&#xff0c;我们经常会遇到数据重复的场景&#xff0c;需要我们对数据进行去重后分析。 以某电商公司的销售报表为例&#xff0c;常见的去重方法我们用到distinct 或者group by 语句&#xff0c; 今天介绍一种新的方法&#xff0c;利用窗口函数…

Github 的使用

3. Github 在版本控制系统中&#xff0c;大约90%的操作都是在本地仓库中进行的&#xff1a;暂存&#xff0c;提交&#xff0c;查看状态或者历史记录等等。除此之外&#xff0c;如果仅仅只有你一个人在这个项目里工作&#xff0c;你永远没有机会需要设置一个远程仓库。只有当你…

2001-2021年全国30省就业人数数据

2001-2021年全国30省就业人数数据/各省就业人数数据 1、时间&#xff1a;2001-2021年 2、范围&#xff1a;包括30个省市不含西藏 3、指标&#xff1a;就业人数 4、来源&#xff1a;各省NJ、社会统计NJ 5、缺失情况说明&#xff1a;无缺失 6、指标说明&#xff1a; 就业人…

实在智能出席第六届数字中国建设峰会,入围2022年信息技术应用创新优秀解决方案榜单

最美榕城四月天&#xff0c;山海之间尽显数字澎湃。这一周来&#xff0c;实在智能来到了“有福之州”&#xff0c;为数字中国建设增添实在色彩。 4月25日&#xff0c;实在华夏行抵达福州站&#xff0c;与众多生态合作伙伴携手共话数字发展新未来&#xff1b; 4月26日&#xff…

分布式事务 --- Seata事务模式、高可用

一、事务模式 1.1、XA模式 XA 规范 是 X/Open 组织定义的分布式事务处理&#xff08;DTP&#xff0c;Distributed Transaction Processing&#xff09;标准&#xff0c;XA 规范 描述了全局的TM与局部的RM之间的接口&#xff0c;几乎所有主流的数据库都对 XA 规范 提供了支持。…

ContextCapture Master 倾斜摄影测量实景三维建模技术应用

查看原文>>>ContextCapture Master 倾斜摄影测量实景三维建模技术应用 目录 第一部分、倾斜摄影测量原理及应用领域 第二部分、倾斜摄影测量数据采集方法 第三部分、CC支持数据类型及导入数据方法 第四部分、CC空三计算参数设置及数据处理方法 第五部分、CC控制…

电气电工相关专业知识及名词解释

一、电流电压 火线、零线、地线&#xff1a;火线和零线的区别就是&#xff1a;火线带电&#xff0c;零线不带电。火线是传电流的&#xff0c;而零线是回流的。 红色是火线&#xff0c;零线一般是绿色的&#xff0c;通常可用电笔来测。电笔一头亮了是火线&#xff0c;不亮的则…

Python使用CV2库捕获、播放和保存摄像头视频

Python使用CV2库捕获、播放和保存摄像头视频 特别提示&#xff1a;CV2指的是OpenCV2&#xff08;Open Source Computer Vision Library&#xff09;&#xff0c;安装的时候是 opencv_python&#xff0c;但在导入的时候采用 import cv2。 若想使用cv2库必须先安装&#xff0c;P…

InnoDB 引擎 底层逻辑

目录 0 课程视频 1 逻辑存储结构 1.1 结构图 1.2 表空间 -> 记录 索引 存储记录 等数据 1.2.1 储存在 cd/var/lib/mysql -> ll -> 目录 mysql.ibd 1.3 段 -> 索引 存储记录 具体存储 1.3.1 数据段 b树 叶子节点 1.3.2 索引段 b树的 非叶子节点 1.3.3 回滚段…

ChatGPT来了不用慌,广告人还有这个神器在手

#ChatGPT能取代广告人吗&#xff0c;#ChatGPT会抢走你的工作吗&#xff1f;#ChatGPT火了&#xff0c;会让营销人失业吗&#xff1f;自ChatGPT爆火以来&#xff0c;各种专业or非专业文章不停给广告人强加焦虑&#xff0c;但工具出现的意义&#xff0c;更多在于提效而非替代&…

【技术分享】防止根据IP查域名,防止源站IP泄露

有的人设置了禁止 IP 访问网站&#xff0c;但是别人用 https://ip 的形式&#xff0c;会跳到你服务器所绑定的一个域名网站上 直接通过 https://IP, 访问网站&#xff0c;会出现“您的连接不是私密连接”&#xff0c;然后点高级&#xff0c;会出现“继续前往 IP”&#xff0c;…

简单分享微信小程序上的招聘链接怎么做

招聘小程序的主要用户就是企业招聘端和找工作人员的用户端,下面从这两个端来对招聘小程序开发的功能进行介绍。 企业端功能 1、岗位发布:企业根据自身岗位需求,在招聘app上发布招聘岗位及所需技能。 2.简历筛选:根据求职者提交的简历选择合适的简历,并对公开发布的简历进行筛…

【五一创作】【Simulink】采用延时补偿的三相并网逆变器FCS-MPC

&#x1f449; 【Simulink】基于FCS-MPC的三相并网逆变器控制 上一篇博客介绍了FCS-MPC的基本操作&#xff0c;并且以三相并网逆变器为控制对象进行了Simulink仿真。 但实际仿真中没有考虑补偿延时。本篇博客将讨论为什么要考虑延时并进行补偿&#xff0c;最后对此仿真验证。 …

【Java数据结构】顺序表、队列、栈、链表、哈希表

顺序表 定义 存放数据使用数组但是可以编写一些额外的操作来强化为线性表&#xff0c;底层依然采用顺序存储实现的线性表&#xff0c;称为顺序表 代码实现 创建类型 先定义一个新的类型 public class ArrayList<E> {int capacity 10; //顺序表的最大容量int size …

【Java笔试强训 6】

&#x1f389;&#x1f389;&#x1f389;点进来你就是我的人了博主主页&#xff1a;&#x1f648;&#x1f648;&#x1f648;戳一戳,欢迎大佬指点! 欢迎志同道合的朋友一起加油喔&#x1f93a;&#x1f93a;&#x1f93a; 目录 一、选择题 二、编程题 &#x1f525;不要二 …

基于微信小程序的垃圾分类系统的研究与实现(附源码和教程)

1. 简介 本文介绍的事基于微信小程序的垃圾分类系统&#xff0c;主要实现的功能有登录、注册、垃圾分类查询、垃圾预约回收、垃圾分类功能。 2.系统设计与实现 本章节是论文的重点&#xff0c;基于上一章介绍的总体设计框架的搭建&#xff0c;详细对小程序的页面布局、流程设…

Photoshop如何使用选区之实例演示?

文章目录 0.引言1.利用快速选择工具抠图2.制作网店产品优惠券3.利用选区改变眼睛颜色4.抠取复杂的花束5.制作丁达尔光照效果6.利用选区调整图像局部颜色 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对PS进行了学习&#xff0c;本文通过《Photoshop2021入门教程》及…

MySQL基础

目标&#xff1a; 掌握MySQL的安装&#xff0c;登录&#xff0c;基础操作 掌握DDL语句 掌握DML语句 掌握DQL语句 1、数据库相关概念 以前我们做系统&#xff0c;数据持久化的存储采用的是文件存储。存储到文件中可以达到系统关闭数据不会丢失的效果&#xff0c;当然文件存储…