IO进程线程(十一)进程间通信 消息队列

文章目录

  • 一、IPC(Inter-Process Communication)进程间通信相关命令 :
    • (一)ipcs --- 查看IPC对象
    • (二)获取IPC键值
    • (三)删除IPC对象的命令
    • (四)获取IPC键值的函数
      • 1. 函数定义
      • 2. 使用示例
  • 二、消息队列
    • (一) 特点
    • (二) 相关API
      • 1. 创建或获取一个消息队列
      • 2. 向消息队列中写消息
      • 3. 在消息队列中读取一条消息
      • 4. 控制消息队列
    • (三) 不关注消息类型
    • (四)关注消息类型
    • (五)消息队列属性结构体

一、IPC(Inter-Process Communication)进程间通信相关命令 :

(一)ipcs — 查看IPC对象

在这里插入图片描述

(二)获取IPC键值

ipcs -q 查看消息队列
在这里插入图片描述

ipcs -m 查看共享内存
在这里插入图片描述

ipcs -s 查看信号灯集
在这里插入图片描述

(三)删除IPC对象的命令

ipcrm -q id 删除消息队列
ipcrm -m id 删除共享内存
ipcrm -s id 删除信号灯集

(四)获取IPC键值的函数

1. 函数定义

#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
功能:
    使用给定的文件和字符来生成一个IPC通信的key值
参数:
    pathname:路径和文件名(必须是已存在的可访问的)
    proj_id:[0-255]的值  一般我们传一个字符即可 如 'A'  'm'
返回值:
    成功  key值
    失败  -1  重置错误码
  • 注:
  • typedef int key_t key_t 即 int 类型
  • pathname 要求必须是一个已存在的文件,因为key值的是由proj_id的后八位,设备号的后8位以及inode号的后16位组成。因此这种机制并不保证key值一定不重复
  • proj_id 只是用了int的一个字节,即取值范围是[0-255],一般使用时传一个字符,字符ASCII码是0-127。

2. 使用示例

生成并打印出key,分析key值的由来

验证代码

#include <my_head.h>

int main(int argc, char const *argv[])
{
    key_t key=0;
    key = ftok("/home/linux/05work",'A');
    printf("my_key = %#x\n",key);

    struct stat file_stat= {0};
    stat("/home/linux/05work",&file_stat);
    printf("proj_id=%#x  dev=%#lx inode=%#lx\n",'A', file_stat.st_dev, file_stat.st_ino);

    return 0;
}

输出结果
在这里插入图片描述

二、消息队列

(一) 特点

  1. 消息队列也是基于内核实现的,存放在内存上(而非硬盘上)。
  2. 消息队列的大小,默认是 16K。
  3. 消息队列中的消息有类型和正文。
    A进程将消息写入消息队列;
    B进程可以根据消息的类型从消息队列中将对应类型的消息取走。
  4. 半双工通信

(二) 相关API

1. 创建或获取一个消息队列

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

int msgget(key_t key, int msgflg);
功能:
    创建或者获取一个消息队列
参数:
    key:键值
        key 通过ftok获取的
        IPC_PRIVATE 表示只有亲缘进程间能用
    msgflg:消息队列的标志位
        IPC_CREAT|0666 消息队列不存在则创建,权限0666 
        或者  
        IPC_CREAT|IPC_EXCL|0666 消息不存在则创建,存在则但会-1,置错误码为EEXIST
返回值:
    成功 消息队列的id
    失败 -1 重置错误码

2. 向消息队列中写消息

int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
功能:向消息队列中写入一条消息
参数:
    msqid:消息队列的id
    msgp: 要写入的数据的首地址
        struct msgbuf {
           long mtype;      /* 消息的类型 必须大于 0 */
           char mtext[1];   /* 消息正文 可以自定义 */
       };
    msgsz:消息正文的大小
    msgflg:标志位 0 阻塞发送  IPC_NOWAIT 非阻塞发送
返回值:
    成功 0
    失败 -1  重置错误码
  • 注:
  • 关于void *msgp参数,第一个long类型大小的空间必须用来存放消息的类型,消息的正文可以自定义
  • size_t msgsz参数,只包含正文数据的大小(sizeof(struct msgbuf)-sizeof(mtype))
  • 阻塞发送的情况下,如果消息队列满了,A进程还想向消息队列中写入消息,此时A进程将会阻塞。

3. 在消息队列中读取一条消息

ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, 
        long msgtyp, int msgflg);
功能:在消息队列中读取一条消息
参数:
    msqid:消息队列的id
    msgp: 用来保存接收的数据的缓冲区的首地址
        struct msgbuf {
           long mtype;     /* 消息的类型 必须大于 0 */
           char mtext[1];  /* 消息正文 可以自定义 */
       };
    msgsz:消息正文的大小
    msgtyp:要接受的消息的类型
        0 :接收消息队列中第一条消息
        >0 : 接收指定类型的第一条消息
        <0 :一般不使用,了解即可,表示接收消息队列中第一条类型最小的小于msgtyp的绝对值的消息
            3-2-5-500-200-8
            读取时,类型传 -200
            读取的顺序  2-3-5 
    msgflg:标志位 0 阻塞接收  IPC_NOWAIT 非阻塞接收
返回值:
    成功 实际读到的正文的字节数
    失败 -1  重置错误码
  • 注:读消息队列和写消息队列中的void *msgp结构体的内部成员要尽量对应。

4. 控制消息队列

int msgctl(int msqid, int cmd, struct msqid_ds *buf);
功能:控制消息队列
参数:
    msqid:消息队列id
    cmd:指令
        IPC_STAT:获取消息队列的属性
        IPC_SET:设置消息队列的属性
        IPC_RMID:立即删除消息队列
            只有消息队列的创建者和所有者以及root用户可以删除消息队列
            msgctl函数的第三个参数被忽略
    buff:属性结构体的地址
返回值:
    成功 0
    失败 -1  重置错误码

(三) 不关注消息类型

此时进程间通信不关心消息类型,按顺序接收第一条消息。
注意,当其中一个进程关闭消息队列后,另一个进程再试图关闭,就会报错,错误码EINVAL

read.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    int type=0;
    while(1){
        if(-1 == msgrcv(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),type,0)){
            ERR_LOG("msgrcv error");
        }
        if(!strcmp(msg.name,"quit")){
            break;
        }
        printf("%ld:%s\n",msg.type,msg.name);
    }
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

write.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    while(1){
        printf("请输入消息:<类型> <正文>:");
        scanf("%ld %s",&msg.type,msg.name);
        msgsnd(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),0);
        if(!strcmp(msg.name,"quit")){
            break;
        }
    }

    //销毁消息队列
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

(四)关注消息类型

此时进程间通信关心消息类型,按顺序接收第一条符合类型的消息,如果消息队列中没有同类型消息,会阻塞等待。
在这里插入图片描述
read.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    int type=0;
    while(1){
        printf("请输入想要接收的消息类型:");
        scanf("%d",&type);
        if(-1 == msgrcv(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),type,0)){
            ERR_LOG("msgrcv error");
        }
        if(!strcmp(msg.name,"quit")){
            break;
        }
        printf("%ld:%s\n",msg.type,msg.name);
    }
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

write.c

#include <my_head.h>

typedef struct msgbuf{
    long type;
    char name[20];
}msg_t;

int main(int argc, char const *argv[])
{
    //获取key值
    key_t key=ftok("/home/linux/05work",'A');

    //创建消息队列
    int msgid = msgget(key,IPC_CREAT|0666);
    if(-1 == msgid)
        ERR_LOG("msgget error");
    //定义消息结构体
    msg_t msg={0};
    while(1){
        printf("请输入消息:<类型> <正文>:");
        scanf("%ld %s",&msg.type,msg.name);
        msgsnd(msgid,&msg,sizeof(msg_t)-sizeof(msg.type),0);
        if(!strcmp(msg.name,"quit")){
            break;
        }
    }

    //销毁消息队列
    if(-1 == msgctl(msgid,IPC_RMID,NULL)){
        if(EINVAL == errno){
            return 0;
        }
        ERR_LOG("msgctl error");
    }
    return 0;
}

(五)消息队列属性结构体

struct msqid_ds {
    struct ipc_perm msg_perm;     /* IPC权限结构体 */
    time_t          msg_stime;    /* 最后一次执行msgsnd的时间 */
    time_t          msg_rtime;    /* 最后一次执行msgrcv的时间 */
    time_t          msg_ctime;    /* 最后一次被修改的时间 */
    unsigned long   __msg_cbytes; /* 当前消息队列中的字节数 */
    msgqnum_t       msg_qnum;     /* 当前消息队列中的消息数 */
    msglen_t        msg_qbytes;   /* 允许的最大字节数 */
    pid_t           msg_lspid;    /* 最后一次执行msgsnd的进程的PID */
    pid_t           msg_lrpid;    /* 最后一次执行msgrcv的进程的PID */
};

struct ipc_perm {
    key_t          __key;       /* 键值 */
    uid_t          uid;         /* 所属用户的id */
    gid_t          gid;         /* 所属用户的组id */
    uid_t          cuid;        /* 创建者的id */
    gid_t          cgid;        /* 创建者的组id */
    unsigned short mode;        /* 权限 */
};
  • 注:
    qbytes可以改小,改大的话需要sudo权限

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

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

相关文章

13 RTP包的使用

RTP RTP包最主要的就是Sequence number。 对于发送者来说&#xff0c;视频的每一个帧都有很多包组成。对于接收端来接收的时候是有一个队列进行接收的。这个队列大小都是通过计算的。有了队列之后就会不断的往队列中插入数据。当队列中有的数据超时一直组不成包的时候&#xf…

k8s离线部署Calico网络(2续)

下载离线镜像 百度网盘 链接&#xff1a;https://pan.baidu.com/s/14ReJW-ZyYZFLbwSEBZK6mA?pwdi6ct 提取码&#xff1a;i6ct 1.将离线镜像上传至所有服务器并解压&#xff1a; [rootmaster ~]# tar xf calico.tar.gz [rootmaster ~]# cd calico 2.所有服务器使用for循环导入…

【微服务】springcloud-alibaba 配置多环境管理使用详解

目录 一、前言 二、配置多环境问题概述 2.1 什么是微服务多环境配置管理 2.1.1 微服务多环境配置管理问题起源 2.2 为什么要做多环境配置管理 2.3 微服务多环境配置管理解决方案 三、springboot 配置多环境管理解决方案 3.1 前置准备 3.1.1 搭建一个springboot工程 3.…

IO流(转换流)

InputStreamReader&#xff08;字符输入转换流 &#xff09; 解决不同编码时&#xff0c;字符流读取文本内容乱码的问题 public static void main(String[] args) {try (//1.得到文件的原始字节流(GBK的字节流形式)FileInputStream is new FileInputStream("src/666.tx…

前端实现点击图片放大查看,并点击关闭

效果展示 HTML 代码 HTML代码比较简单&#xff0c;包含了一个img元素&#xff0c;用显示原有图片&#xff0c;和一个模态框元素div&#xff0c;用于显示放大之后的图片元素&#xff0c;模拟模态框的样式 <!DOCTYPE html> <html lang"en"> <head>…

Vue3全局封装dialog弹框

Vue3全局封装modal弹框使用&#xff1a; 应用场景&#xff1a;全局动态form表单弹框 应用Vue3碎片&#xff1a; ref&#xff0c;reactive&#xff0c;app.component&#xff0c;defineExpose&#xff0c;defineProps&#xff0c;defineEmits 应用UI: element-plus dialog form …

MySQL系列-安装配置使用说明(MAC版本)

1、前言 本文将介绍MySQL的安装配置以及基本语法操作说明 环境&#xff1a;mac 版本&#xff1a;MySQL 8.0.28 之前电脑安装卸载过&#xff0c;后面在装的时候遇到一些问题&#xff0c;用了四五天才解决&#xff0c;主要是参考 https://blog.csdn.net/zz00008888/article/deta…

动态内存管理(malloc,calloc,realloc,free)+经典笔试题

动态内存管理 一. malloc 和 free1. malloc2. free 二. calloc三. realloc四.动态内存的错误1.对NULL指针的解引用操作2.对动态开辟空间的越界访问3.对非动态开辟内存使用free释放4.使用free释放一块动态开辟内存的一部分5.对同一块动态内存多次释放6.动态开辟内存忘记释放&…

streamlit:如何快速构建一个应用,不会前端也能写出好看的界面

通过本文你可以了解到&#xff1a; 如何安装streamlit&#xff0c;运行起来第一个demo熟悉streamlit的基本语法&#xff0c;常用的一些组件使用streamlit库构建应用 大模型学习参考&#xff1a; 大模型学习资料整理&#xff1a;如何从0到1学习大模型&#xff0c;搭建个人或企业…

SQL Chat:从SQL到SPEAKL的数据库操作新纪元

引言 SQL Chat是一款创新的、对话式的SQL客户端工具。 它采用自然语言处理技术&#xff0c;让你能够像与人交流一样&#xff0c;通过日常对话的形式对数据库执行查询、修改、创建及删除操作 极大地简化了数据库管理流程&#xff0c;提升了数据交互的直观性和效率。 在这个框…

【漏洞复现】用友NC pagesServlet SQL注入漏洞(XVE-2024-13067)

0x01 产品简介 用友NC是由用友公司开发的一套面向大型企业和集团型企业的管理软件产品系列。这一系列产品基于全球最新的互联网技术、云计算技术和移动应用技术&#xff0c;旨在帮助企业创新管理模式、引领商业变革。 0x02 漏洞概述 用友NC /portal/pt/servlet/pagesServlet…

关于 Redis 中集群

哨兵机制中总结到&#xff0c;它并不能解决存储容量不够的问题&#xff0c;但是集群能。 广义的集群&#xff1a;只要有多个机器&#xff0c;构成了分布式系统&#xff0c;都可以称之为一个“集群”&#xff0c;例如主从结构中的哨兵模式。 狭义的集群&#xff1a;redis 提供的…

MySQLWorkbench导出sql文件

MySQLWorkbench导出sql文件 前言效果图导出操作选择要导出的数据库遇到的问题解决问题查看mysql路径前言 在完成数据库搭建之后,需要为上线做准备,那么就需要导出数据库的建库sql了 本篇文章讲解的是mysql Workbench 导出数据建库脚本 效果图 导出操作 选择要导出的数据库…

java 深拷贝和浅拷贝

深拷贝和浅拷贝的概念 浅拷贝&#xff1a;只对基本数据类型进行拷贝&#xff0c;针对于引用数据类型&#xff0c;只是拷贝了对象的引用&#xff0c;没有真实的创建一个新的对象就是浅拷贝 深拷贝&#xff1a;在对引用数据类型拷贝过程中&#xff0c;创建了一个新的对象&#xf…

Dell服务器根据GPU温度调整风扇转速

前言 dell服务器自动风扇是根据CPU温度来调速的&#xff0c;我跑AI的时候cpu温度不高但是GPU温度很高导致显卡卡死PVE虚拟机直接挂起无法运行&#xff0c;我看了下也没有基于显卡温度调速的脚本&#xff0c;于是我就自己写了一个 基于ipmi工具 乌班图等linux先安装ipmi apt …

算法金 | A - Z,115 个数据科学 机器学习 江湖黑话(全面)

大侠幸会&#xff0c;在下全网同名「算法金」 0 基础转 AI 上岸&#xff0c;多个算法赛 Top 「日更万日&#xff0c;让更多人享受智能乐趣」 机器学习本质上和数据科学一样都是依赖概率统计&#xff0c;今天整整那些听起来让人头大的机器学习江湖黑话 A - C A/B Testing (A/B …

电子电器架构——智能座舱设备终端

我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 屏蔽力是信息过载时代一个人的特殊竞争力,任何消耗你的人和事,多看一眼都是你的不对。非必要不费力证明自己,无利益不试图说服别人,是精神上的节…

电路笔记 : 嘉立创EDA 导入、查找、设计管理器(快速寻找网络标签)功能+DRC错误检查和处理

导入功能 查找功能 可查找多种类型&#xff0c;如原件名称、网络标签等 设计管理器 图层查看 DRC错误 规则设置 线距问题 大多数PCB制造商能够可靠地生产5 mil间距的走线和间隙。这是一个常见的标准&#xff0c;适合大多数消费级和工业级电子产品。在5 mil以上的间距&#xff…

设计软件有哪些?照明工具篇,渲染100邀请码1a12

阴影和照明涉及到图片的真实感和氛围&#xff0c;所以熟练使用照明工具是设计师的必备能力&#xff0c;这次我们介绍一些照明工具。 1、VRaySun VRaySun是VRay渲染器中的一个功能&#xff0c;用于模拟太阳光源。它是一种方便易用的光源类型&#xff0c;能够产生逼真的日光效果…

MySQL与PostgreSQL关键对比三(索引类型)

目录 索引类型 B-tree 索引 Hash 索引 Full-text 索引 GiST 索引 GIN 索引 BRIN 索引 索引创建示例 MySQL PostgreSQL 结论 以下SQL语句的执行如果需要开发工具支持&#xff0c;可以尝试使用SQLynx或Navicat来执行。 MySQL和PostgreSQL在索引方面有许多相似之处&am…