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

news2024/11/13 18:49:54

前面提到的管道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);//实现移除队列

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

 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

vulnhub靶机Misdirection

环境准备 下载链接&#xff1a;https://download.vulnhub.com/misdirection/Misdirection.zip 解压后双击ovf文件导入虚拟机 网络&#xff1a;DHCP、NAT、192.168.100.0/24网段 信息收集 主机发现 192.168.100.133是新增的ip 端口扫描 发现开放了以上端口&#xff0c;继续…

【Java笔试强训 28】

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

【Python入门】Python搭建编程环境-安装Python3解释器(内含Windows版本、MacOS版本、Linux版本)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!!

与其焦虑被 AI 取代或猜测前端是否已死, 不如看看 vertical-align 扎实你的基础!!! vertical-align 设置 display 值为 inline, inline-block 和 table-cell 的元素竖直对齐方式. 从 line-height: normal 究竟是多高说起 我们先来看一段代码, 分析一下为什么第二行的行高, 也就…

D. Mysterious Crime(单个位置贡献)

Problem - D - Codeforces Acingel是一个小镇。这里只有一位医生——Miss Ada。她非常友善&#xff0c;没有人曾经对她说过坏话&#xff0c;所以谁能想到Ada会在她的房子里被发现死亡&#xff1f;世界著名侦探Gawry先生被任命查找罪犯。他询问Ada的邻居关于那个不幸的日子里拜访…

Java回收垃圾的基本过程与常用算法

目录 一、基本概述 二、垃圾分类 基本背景 举例说明各种引用类型的作用 强引用&#xff08;Strong Reference&#xff09; 软引用&#xff08;Soft Reference&#xff09; 弱引用&#xff08;Weak Reference&#xff09; 虚引用&#xff08;Phantom Reference&#xff…

广搜的优化技巧(备赛中)

A.电路维修 这道题我们对于每一个点都有四个方向&#xff0c;分别为 char op[]{"\\/\\/"}; 如果我们当前点到下一个点的方向不是对应的方向时我们的distance就加1&#xff0c;因为我们要求最优距离&#xff0c;所以我们采取一个小贪心的法则&#xff0c;每一次我们将…

「神州数码DCN」SAVI在IPV6环境下的应用

前言 介绍 ISIS&#xff0c;中间系统到中间系统的网络协议&#xff0c;最初是OSI组织为了他的CLNP&#xff08;类似于TCP/IP中的IP网络&#xff09;而设计的动态路由协议&#xff0c;后IETF对其进行修改和填充&#xff0c;现可以在TCP/IP和OSI环境中使用&#xff0c;称为&…

JavaWeb学习------jQuery

JavaWeb学习------jQuery jQuery函数库下载 jQuery函数库下载官网&#xff1a;Download jQuery | jQuery配套资料&#xff0c;免费下载 链接&#xff1a;https://pan.baidu.com/s/1aXBfItEYG4uM53u6PUEMTg 提取码&#xff1a;6c9i 然后下载&#xff1f; 来到官网&#xf…

Spark 1:Spark基础入门

Spark是什么 定义&#xff1a;Apache Spark是用于大规模数据&#xff08;large-scala data&#xff09;处理的统一&#xff08;unified&#xff09;分析引擎。 Spark 借鉴了 MapReduce 思想发展而来&#xff0c;保留了其分布式并行计算的优点并改进了其明显的缺陷。让中间数据存…

Winform从入门到精通(36)—ColorDialog(史上最全)

文章目录 前言一、属性1、AllowFullOpen2、AnyColor3、Color4、FullOpen5、ShowHelp6、SolidColorOnly7、Tag二、事件1、HelpRequest前言 当我们需要设置某个控件的颜色时,并且需要弹出一个可以选择颜色的对话框时,这时候就需要使用ColorDialog 一、属性 1、AllowFullOpen…

详解正则化

&#xff08;一&#xff09;正则化目的 防止过拟合现象&#xff0c;通过降低模型在训练集上的精度来提高其泛化能力&#xff0c;从而增加正则项 常见的降低过拟合方法 ■增加数据集的数据个数。数据量太小时&#xff0c;非常容易过拟合&#xff0c;因为 小数据集很容易精确拟…

Linux线程相关函数:线程的创建、回收、退出、取消

1. 线程号 进程号在系统中唯一&#xff0c;但线程号只在其所属进程环境中有效。 &#xff08;1&#xff09;pthread_self函数 #include<pthread.h>pthread_t pthread_self(void); /* 功能&#xff1a;获取线程号 返回值&#xff1a;调用此函数线程的ID */ pthread_se…

基于ssm的论坛系统的设计与实现【附源码】

基于ssm的论坛系统的设计与实现 摘 要 早期的网络论坛系统已经诞生一段时间&#xff0c;随着互联网技术的发展&#xff0c;它已经从最初的简单电子公告板系统变成了一种丰富的论坛系统社区模型。人们通过论坛系统进行信息的获取、发布和交流已经成为一种普遍的社交方式&#x…

一键开关机电路

一键开关机电路&#xff0c;通常用在防止关机导致数据保存发生错误&#xff0c;特别是在写EEPROM&#xff0c;FLASH和SD卡时&#xff0c;如果正在写入数据时断电&#xff0c;可能会导致数据保存错误&#xff0c;甚至导致元件损坏。一键开关机电路是由CPU来掌控&#xff0c;决定…

零基础带你认识HTML常用标签

目录 HTML 结构认识 HTML 标签HTML 文件基本结构标签层次结构快速生成代码框架 HTML 常见标签注释标签标题标签&#xff1a;h1 - h6水平线 hr 标签段落标签: p换行标签: br格式化标签图片标签: imgimg 标签的其他属性 超链接标签: a 表格标签基本使用和并单元格 列表标签表单标…

Compiler Lab1- 自制词法分析器

由于编译原理课的Lab1为自制词法分析器&#xff0c;所以笔者用C实现了一个极简的C语言词法分析器&#xff0c;用于分析C语言源代码。它可以处理关键字、标识符、整数、实数、浮点数的科学计数法表示、运算符、分隔符、字符串字面量、字符字面量、注释和预处理指令。请注意&…

【五一创作】力扣刷题实录(大厂用题)—— 1. 打家劫舍

1. 打家劫舍 某大厂 2022 年 9 月面试题挑战&#xff08;三&#xff09; 1.1 题目描述 力扣真题地址&#xff1a;https://leetcode.cn/problems/house-robber/?envTypestudy-plan-v2 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;…

Photoshop如何使用路径与矢量工具之实例演示?

文章目录 0.引言1.制作名片2.利用钢笔工具抠出复杂图像3.制作App图标4.制作软件登录界面5.将图片切成九宫格 0.引言 因科研等多场景需要进行绘图处理&#xff0c;笔者对PS进行了学习&#xff0c;本文通过《Photoshop2021入门教程》及其配套素材结合网上相关资料进行学习笔记总结…

简单搭建node后台(笔记用)

毕设过程 mongodb 配置 使用node写后台一些语法运用bug关于安装一款群控软件后&#xff0c;修改了环境变量导致后台崩溃![](https://img-blog.csdnimg.cn/7c684b2e318048b3ad1db78484e10e6a.jpeg) vue管理后台 mongodb 配置 https://blog.csdn.net/weixin_43405300/article/de…