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

news2024/11/26 5:48:39

文章目录

  • 一、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.coloradmin.cn/o/1808938.html

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

相关文章

[office] 快速提取出Excel 2010单元格括号内的文字信息 #知识分享#经验分享

快速提取出Excel 2010单元格括号内的文字信息 实例演示 ①我们打开一份Excel电子表格&#xff0c;我们要将C列里面括号内的内容提取到D列里面&#xff0c;单击D2单元格&#xff0c;输入下面的函数公式&#xff1a; MID(C2,FIND("(",C2)1,LEN(C2)-FIND("("…

vue如何使用slot

1. vue2 如何使用slot 1.1. 默认插槽&#xff08;Default Slot&#xff09;1.2. 具名插槽&#xff08;Named Slot&#xff09;1.3. 作用域插槽&#xff08;Scoped Slot&#xff09; 2. vue3 如何使用slot 2.1. 默认插槽&#xff08;Default Slot&#xff09;2.2. 具名插槽&…

Pytorch学习11_神经网络-卷积层

1.创建神经网络实例 import torch import torchvision from torch import nn from torch.nn import Conv2d from torch.utils.data import DataLoaderdatasettorchvision.datasets.CIFAR10("../dataset_cov2d",trainFalse,transformtorchvision.transforms.ToTensor(…

【深度学习】NLP,Transformer讲解,代码实战

文章目录 1. 前言2. Transformer结构训练过程1. 输入嵌入和位置编码2. 编码器层2.1 单头的注意力机制(便于理解)2.2 多头的注意力机制(Transformer真实使用的)2.3 残差连接和层归一化2.4 前馈神经网络&#xff08;FFN&#xff09;2.5 残差连接和层归一化2.6 总结 3. 解码器层 推…

本周重磅日程:美联储决议、中美通胀、苹果AI和英伟达拆股

当周重磅看点颇多&#xff1a;美联储FOMC将公布最新利率“点阵图”&#xff0c;中国5月金融数据、中美通胀数据将出炉&#xff0c;日本央行购债计划是否变动成为市场焦点&#xff0c;苹果2024全球开发者大会一系列AI功能将亮相&#xff1b;特斯拉2024股东大会上马斯克560亿美元…

Linux 内核参数-相关介绍

Linux 内核参数-相关介绍 今天&#xff0c;介绍Linux内核参数相关内容。由于Linux内核优化需要根据具体需求进行具体优化&#xff0c;同时需要具备一定经验&#xff0c;所以这里不涉及优化操作内容。 不过&#xff0c;遇到面试中有相关题目&#xff0c;不至于答不上来&#x…

Android.mk文件生成的so工程文件并Debug调试native code

1.这里主要展示一下从最原始先新建一个工程 2.将hello的子工程文件放入上面新建好的工程里面&#xff0c;直接拷贝放置这里 3.修改根目录下的settings.gradle 加入hello 4.app工程下的build.gradle加入依赖&#xff0c;这样就可以识别hello中的java包文件 5.MainActivity 中来&…

python tushare股票量化数据处理:学习中

1、安装python和tushare及相关库 matplotlib pyplot pandas pandas_datareader >>> import matplotlib.pyplot as plt >>> import pandas as pd >>> import datetime as dt >>> import pandas_datareader.data as web 失败的尝试yf…

vscode侧边栏错乱重制

vscode 重制命令面板 View: Reset View Locations

将AIRNet集成到yolov8中,实现端到端训练与推理

AIRNet是一个图像修复网络,支持对图像进行去雾、去雨、去噪声的修复。其基于对比的退化编码器(CBDE),将各种退化类型统一到同一嵌入空间;然后,基于退化引导恢复网络(DGRN)将嵌入空间修复为目标图像。可以将AIRNet的输出与yolov8进行端到端集成,实现部署上的简化。 本博…

LabVIEW汽车电机测试系统

1. 背景 随着电动汽车的快速发展&#xff0c;汽车电机作为电动汽车的核心部件&#xff0c;其性能评估变得尤为重要。电机的功率、效率、转速等参数直接影响着电动汽车的性能和续航里程。因此&#xff0c;设计一套全面、准确的汽车电机测试系统对于提高电动汽车的性能和安全性具…

基于Java-SpringBoot-VUE-MySQL的企业财务报销系统

基于Java-SpringBoot-VUE-MySQL的企业财务报销系统 登陆界面 联系作者 如需本项目源代码&#xff0c;可扫码或者VX:bob1638 联系作者。 主页-02 系统功能持续更新中。。。 介绍 本系统是采用现代信息技术手段&#xff0c;采用JAVA开发语言&#xff0c;VUE语言&#xff0c;HTML语…

【C++进阶】深入STL之 栈与队列:数据结构探索之旅

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;模拟实现list与迭代器 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀stack和queue &#x1f4…

基础数据结构 -- 堆

1. 简介 堆可以看做是一种特殊的完全二叉树&#xff0c;它满足任意节点的值都大于或小于其子节点的值。 2. 功能 插入元素&#xff1a;插入新元素时&#xff0c;先将元素放至数组末尾&#xff0c;然后通过上浮算法自底向上调整&#xff0c;使堆保持性质。删除堆顶元素&#xff…

Unity DOTS技术(九) BufferElement动态缓冲区组件

文章目录 一.简介二.例子 一.简介 在之前的学习中我们发现Entity不能挂载相同的组件的. 当我们需要用相同的组件时则可以使用.IBufferElementData接口 动态缓冲区组件来实现 二.例子 1.创建IBufferElementData组件 using Unity.Entities; using UnityEngine; //[GenerateAu…

BIOPLUSS引领膳食行业创新、整合与再造

2024年NHNE如期而至&#xff0c;同时今年也是中挪建交70年周年&#xff0c;BIOPLUSS作为挪威品牌代表也参加了此次NHNE国际健康营养博览会&#xff0c;此次NHNE展会吸收了来自30多个国家及地区的1200多家品牌参与&#xff0c;BIOPLUSS同时受挪威领事馆、挪威创新署邀请&#xf…

安卓事件交互(按键事件、触摸事件、手势识别、手势冲突处理)

本章介绍App开发常见的以下事件交互技术&#xff0c;主要包括&#xff1a;如何检测并接管按键事件&#xff0c;如何对触摸事件进行分发、拦截与处理&#xff0c;如何根据触摸行为辨别几种手势动作&#xff0c;如何正确避免手势冲突的意外状况。 按键事件 本节介绍App开发对按…

Duck Bro的第512天创作纪念日

Tips&#xff1a;发布的文章将会展示至 里程碑专区 &#xff0c;也可以在 专区 内查看其他创作者的纪念日文章 我的创作纪念日第512天 文章目录 我的创作纪念日第512天一、与CSDN平台的相遇1. 为什么在CSDN这个平台进行创作&#xff1f;2. 创作这些文章是为了赚钱吗&#xff1f…

Nginx服务配置

一、Nginx服务的主配置文件nginx.conf vim /usr/local/nginx/conf/nginx.conf 全局块&#xff1a;全局配置&#xff0c;对全局生效&#xff1b;events块&#xff1a;配置影响 Nginx 服务器与用户的网络连接&#xff1b;http块&#xff1a;配置代理&#xff0c;缓存&#xff0c…

WordPress 高级缓存插件 W3 Total Cache 开启支持 Brotli 压缩算法

今天明月给大家分享一下 WordPress 高级缓存插件 W3 Total Cache 开启支持 Brotli 压缩算法的教程&#xff0c;在撰写【WordPress 高级缓存插件 W3 Total Cache Pro 详细配置教程】一文的时候明月就发现 W3 Total Cache 已经支持 Brotli 压缩算法了&#xff0c;可惜的是在安装完…