【Linux】进程间通信 -- System V共享内存

news2024/11/24 17:21:06

前言

本篇博客介绍第二种进程间通信的方式 – System V
System V 有三种方式:
共享内存
消息队列
信号量
本篇博客对于系统调用的函数,会进行一定的封装

在这里插入图片描述

文章目录

  • 前言
  • 一. System V 共享内存
  • 二. 共享内存的原理
  • 三. 共享内存的创建
  • 四. 共享内存的查看和删除(指令)
  • 五. 共享内存的修改/删除
  • 六. 共享内存的权限
  • 七. 共享内存的关联
  • 八. 共享内存的取关联
  • 九. 共享内存的通信
  • 结束语

一. System V 共享内存

二. 共享内存的原理

进程间具有独立性,不同进程之间无法直接获取对方的数据,所有通信都需要有第三方的介入,管道是以文件的方式,提供共享的文件。
匿名管道建立在子进程继承父进程的进程信息的特性,可以获得父进程创建的匿名管道的文件描述符,仅适用于具有“亲戚关系”的进程;
命名管道本质同匿名管道一样,只不过是在当前目录下创建一个管道文件,不同进程可以以不同方式打开这个管道文件,进行读写适用于任何不同进程
两个管道都是内存级文件不会进行刷盘

那么System V 共享内存是以什么方式使得不同进程进行通信的呢?

进程间通信的前提都是:让不同进程看到同一份“资源”
管道通过文件,而System V通过内存实现
在这里插入图片描述
System V创建的共享内存通过页表映射放到不同进程的共享区

三. 共享内存的创建

创建共享内存的函数是shmget() shared memory get
在这里插入图片描述

  1. 第一个参数稍后详细讲解
  2. 第二个参数是创建的共享内存的大小
  3. 第三个参数是一个位图,可传的参数有三种情况:
    (1) IPC_CREAT:没有共享内存就创建,有,就返回这个共享内存的shmid
    (2) IPC_CREAT | IPC_EXCL :没有共享内存就创建,有,就直接报错,返回-1并设置错误码。其作用是确保创建的共享内存是最新的
    (3) 以上两种 | 权限
    注意:IPC_EXCL不能单独使用,没有效果

返回值是创建的共享内存的共享内存的标识符(和文件描述符不同)

下面我们详细讲解一下第一个参数

我们创建的共享内存,需要不同进程双方都能找到,并且创建新的共享内存时不和其他共享内存冲突,所有需要由系统生成一个key值,尽量的避免冲突,保证不同进程的通信

生成key值的函数是:ftok()
在这里插入图片描述
内存中可能同时有多个共享内存,操作系统也会对其有相应的结构体,对他们进行管理。这也是先描述,再组织思想的体现。
所以我们就需要确保不同进程能找到同一个共享内存

第一个参数:路径字符串;第二个参数:项目ID
ftok的这两参数,在创建后会保存在共享内存结构体的属性中,这样操作系统才能帮我们找到我们要使用的共享内存。
而需要进行通信的进程,需要确保这两个参数传的一样,才能找到同一个共享内存。
具体内容是什么其实不重要(pathname需要是能找到的文件路径),只要确保要通信的进程传的一样就好

获取key值的函数

#define PATHNAME "." 
#define PROJID 0x6666

//获取key值
key_t getKey()
{
    key_t k=ftok(PATHNAME,PROJID);
    if(k==-1)
    {
        cerr<<errno<<":"<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}

//将key值转换成十六进制
string toHex(key_t k)
{
    char buffer[64];
    snprintf(buffer,sizeof(buffer),"0x%x",k);
    return buffer;
}

简单测试一下
在这里插入图片描述

这样我们就得到了相同的key值。
接下来就是创建内存空间了。

因为创建共享内存和获取共享内存只有最后一个参数有所不同,所以我们可以进行一定的封装

//进行封装
static int createShmHelper(key_t key,int size,int flag)
{
    int shmid = shmget(key,size,flag);

    //出错判断
    if(shmid==-1)
    {
        cerr<<"error: "<<errno<<" : "<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
}

//创建共享内存
int createShm(key_t key,int size)
{
    //因为是创建,我们就用,保证创建最新的内存空间
    return createShmHelper(key,size,IPC_CREAT | IPC_EXCL);
}

//获取共享内存
int getShm(key_t key,int size)
{
    //只要获取就好,所以只要IPC_CREAT
    return createShmHelper(key,size,IPC_CREAT);
}

简单测试一下
在这里插入图片描述

四. 共享内存的查看和删除(指令)

我们成功创建了共享内存,那么接下来有个疑问:我们这两个程序很简单,没有任务循环,运行一下就结束了,那相应创建的共享内存还存在吗?
我们如果再运行shmServer会发现以下现象
在这里插入图片描述
因为在shmServer.cpp中,我们调用shmget()使用的是IPC_CREAT | IPC_EXCL,所以当共享内存已经存在时,会创建失败,返回-1,设置错误码。

==================================================================================

共享内存的查看

我们还可以使用ipcs指令查看进程间通信的相关信息
在这里插入图片描述
第一个是消息队列,第二个共享内存,第三个是信号量数组
ipcs -m指令表示只查看共享内存
在这里插入图片描述

key:共享内存对应的key值,类比文件inode编号,内核使用
shmid:共享内存的id,类比文件描述符fd,用户使用
owner:创建该共享内存的用户
perms:该共享内存的权限(同文件)
bytes:大小(以字节为单位)
nattch:关联(链接)该共享内存的进程数
status

以上两种现象,都表明,即使创建共享内存的进程已经退出,相应的共享内存其实还存在。

==================================================================================

共享内存的删除
我们可以通过ipcrm -m shmid删除我们创建的共享内存
在这里插入图片描述
我们再创建共享内存就会分配新的shmid给我们创建的共享内存了
在这里插入图片描述

五. 共享内存的修改/删除

函数是:shmctl()
在这里插入图片描述
第一个参数是shmid;第二个是对共享内存的操作,操作有多种,删除,修改个别属性...;第三个参数是输出型参数,是内核中管理共享内存的结构体,可以通过传结构体获取该共享内存的属性

这是结构体的部分属性
在这里插入图片描述

共享内存的删除
修改操作的宏是IPC_RMID

//删除共享内存
void delShm(int shmid)
{

    int n=shmctl(shmid,IPC_RMID,NULL);
    assert(n!=-1);
    (void)n;
}

==================================================================================

获取共享内存属性

//查看共享内存属性
struct shmid_ds ds;
int n=shmctl(shmid,IPC_STAT,&ds);
if(n!=-1)
{
    cout<<"perm: "<<toHex(ds.shm_perm.__key)<<endl;
    cout<<"create pid: "<<ds.shm_cpid<<" : "<<getpid()<<endl;
}

在这里插入图片描述
注意:查看共享内存需要有权限
调用者必须要有这个共享内存的读权限
在这里插入图片描述

六. 共享内存的权限

我们用以上方法创建一个共享内存,perms是权限,默认为0
在这里插入图片描述

权限需要在创建时确定,其实只需要在shmget的第三个参数加上对应的权限就好

//创建共享内存
int createShm(key_t key,int size)
{
    //因为是创建,我们就用,保证创建最新的内存空间
    umask(0);//将掩码设置为0,保证预期的权限
    return createShmHelper(key,size,IPC_CREAT | IPC_EXCL | 0666);
}

在这里插入图片描述
这样就成功设置权限了。

七. 共享内存的关联

以上我们完成了进程间通信的周边工作,但是我们还没有真正实现通信。我们只创建了共享内存,但其实此时共享内存还并没有在我们进程的task struct中,我们只是成功的创建了。为此我们还需要关联这个共享内存,才会将共享内存映射到进程的共享区,才可以真正的使用。

关联的函数是shmat()
在这里插入图片描述

第一个参数是shmid;第二个参数是共享区的虚拟地址,可以通过这个参数指定将共享内存挂接到指定位置;第三个参数是挂接方式(读写)
返回值是挂接的共享区的虚拟地址

//关联共享内存
char * attchShm(int shmid)
{
    char*start=(char*)shmat(shmid,NULL,0);
    return start;
}

在这里插入图片描述
在(三)中,我们就讲了,nattch是关联该共享内存的进程数,这个测试也验证了这一点。0->1->2->1->0

八. 共享内存的取关联

取关联的函数是shmdt()

头文件和shmat()一样
在这里插入图片描述
传参是shmat的返回值

//取关联共享内存
void detachShm(char*start)
{
    int n=shmdt(start);
    assert(n!=-1);
    (void)n;
}

九. 共享内存的通信

接下来,我们就可以使用共享内存进行通信了。
不过我们还可以将上述的代码再进行一个封装,封装成一个类

#define SERVER 1
#define CLIENT 0
const int gsize=4096;

class Init
{
public:
    Init(int t):type(t)
    {
        //获取key值
        key_t k = getKey();
        //创建/获取共享内存
        if(type == SERVER)
        {
            //服务端创建
            shmid = createShm(k, gsize);
        }
        else
        {
            //客户端获取
            shmid = getShm(k, gsize);
        }
        //关联共享内存
        start = attachShm(shmid);
    }

    //获取读写字符串
    char *getStart()
    { 
        return start; 
    }

    ~Init()
    {
        //取关联
        detachShm(start);

        if(type == SERVER)
        {
            //如果是服务端,还需要销毁共享内存
            delShm(shmid);
        }
    }
private:
    char *start;//读写字符串
    int type; //表示是服务端还是客户端
    int shmid;//共享内存的id
};

使用如下:
在这里插入图片描述
在这里插入图片描述
成功完成通信

结束语

本篇知识记录较杂,请多谅解。本着记笔记分享的目的,望佬指点。

如果觉得本篇文章对你有所帮助的话,不妨点个赞支持一下博主,拜托啦,这对我真的很重要。
在这里插入图片描述

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

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

相关文章

MinGW MinGW-W64介绍

Table of content 0 Preface/Foreword 1 MinGW-w64 1.1 使用MinGW-w64的原因 1.2 MinGW-w64使用场景 1.3 官网 2 GCC & LLVM 2.1 编译器构成 2.2 GCC 2.3 LLVM 2.3.1 Clang 0 Preface/Foreword MInGW全称为&#xff1a;Minimalist GNU on Windows.将经典的开源C…

Unity VFX -- (4)创建burst粒子效果

如果用户成功达成某个目标&#xff0c;我们可以使用一个爆裂的礼花来激励用户。如果角色挥舞刀剑&#xff0c;我们可以做出剑气来增加气势。如果角色落到地面上&#xff0c;我们可以在脚部做出飞舞的灰尘来增加表现力。这些都可以视为burst粒子效果。 下面是一些burst粒子效果&…

JVM学习(六):类加载子系统

目录 〇、前言 一、类加载子系统 1.1 内存结构概述 1.2 类加载器及类加载过程概述 1.2.1 类加载器 1.2.2 类加载过程 1.3 类加载过程一&#xff1a;Loading 1.3.1 加载过程 1.3.2 加载类的方式 1.4 类加载过程二&#xff1a;Linking 1.4.1 验证(Verify) 1.4.…

贯穿设计模式第八话--设计原则总结篇

&#x1f973;&#x1f973;&#x1f973; 茫茫人海千千万万&#xff0c;感谢这一刻你看到了我的文章&#xff0c;感谢观赏&#xff0c;大家好呀&#xff0c;我是最爱吃鱼罐头&#xff0c;大家可以叫鱼罐头呦~&#x1f973;&#x1f973;&#x1f973; 从今天开始&#xff0c;将…

在uos上编译opencv

作者&#xff1a;朱金灿 来源&#xff1a;clever101的专栏 为什么大多数人学不会人工智能编程&#xff1f;>>> 下载源码并创建build文件夹 系统环境为操作系统为&#xff1a;UnionTech OS Server 20 Enterprise&#xff0c;处理器为: 华为鲲鹏处理器&#xff08;ar…

C++11(上)

目录 1&#xff1a;列表初始化 2&#xff1a;std::initializer_list 3:变量类型推导 3.1:auto推导类型 3.2:decltype 3.3:nullptr 4:范围for 5:STL新增容器和容器新增接口 5.1:array 6:左值引用和右值引用 6.1:左值 6.2:右值 6.3:左值引用 6.4:右值引用 6.5:左值…

python常用库之time库

目录 一、前言time库中的常用函数 二、time()函数三、localtime()和gmtime()函数四、strftime() 、asctime()、mktime()函数&#xff08;一&#xff09;strftime()函数&#xff08;二&#xff09;asctime()函数&#xff08;三&#xff09;mktime()函数 五、ctime()函数六、stri…

【2023最新】超详细图文保姆级教程:App开发新手入门(5)

上文回顾&#xff0c;我们已经完成了一个应用的真机调试&#xff0c;本章我们来了解一下如何引入YonBuilder移动开发的&#xff08;原生&#xff09;移动插件, 并利用移动插件完成一个简单的视频播放器。 8. 「移动插件」的使用 8.1 什么是 「移动插件」&#xff1f; 用通俗…

TensorFlow Lite,ML Kit 和 Flutter 移动深度学习:1~5

原文&#xff1a;Mobile Deep Learning with TensorFlow Lite, ML Kit and Flutter 协议&#xff1a;CC BY-NC-SA 4.0 译者&#xff1a;飞龙 本文来自【ApacheCN 深度学习 译文集】&#xff0c;采用译后编辑&#xff08;MTPE&#xff09;流程来尽可能提升效率。 不要担心自己的…

【高危】Apache Spark 权限提升漏洞(CVE-2023-22946)

漏洞描述 Apache Spark 是一款支持非循环数据流和内存计算的大规模数据处理引擎。 使用 spark-submit 脚本在集群中启动任务可以通过指定proxy-user参数限制运行用户。在 Apache Spark 受影响版本中&#xff0c;攻击者通过指定自定义的classpath&#xff0c;则可以覆盖该配置…

2023最新面试题-Java-3

IO流 1. java 中 IO 流分为几种? 按照流的流向分&#xff0c;可以分为输入流和输出流&#xff1b;按照操作单元划分&#xff0c;可以划分为字节流和字符流&#xff1b;按照流 的角色划分为节点流和处理流。 Java Io 流共涉及 40 多个类&#xff0c;这些类看上去很杂乱&…

ChatGPT 70+款可以免费使用的AI工具,建议收藏

ChatGPT风靡全球&#xff0c;人人可用&#xff01; 小红书上有关ChatGPT的笔记已有10w篇&#xff0c;相关话题浏览量也达到了1.12亿次。其中讨论最为热烈的&#xff0c;要数“ChatGPT使用教程”。&#xff08;当然&#xff0c;类似的话题还包括&#xff0c;教你如何使用Midjour…

Navicat图表查看器 Crack

Navicat图表查看器 Crack Navicat图表查看器是一个查看图表工作区文件的简单工具。您可以浏览Navicat的图表工具和Navicat图表创建者创建的区域。 Navicat图表查看器&#xff0c;将图表中的数据显示为强大的可视化效果&#xff0c;允许您使用图形和图表查看数据。 将您的信息转…

Internet Download Manager(IDM)v6.41.11 免激活不弹窗版

Internet Download Manager&#xff08;IDM&#xff09;v6.41.11 免激活不弹窗版可提升你的下载速度多达5倍&#xff0c;安排下载时程&#xff0c;或续传一半的软件。Internet Download Manager的续传功能可以恢复因为断线、网络问题、计算机宕机甚至无预警的停电导致下传到一半…

大数据开发必备面试题Flume篇合集

大数据开发必备面试题Flume篇合集 1 、详细介绍Flume有哪些组件&#xff1f;2、你是如何实现Flume数据传输的监控的&#xff1f;3、Flume参数怎么调优&#xff1f;4、简述下Flume的事务机制。5、 Flume采集数据会丢失吗?6、简述下Flume使用场景。7、简述下 Flume丢包问题。8、…

【C语言】文件操作

目录 1.为什么使用文件 2.什么是文件 2.1 程序文件 2.2 数据文件 2.3文件名 3.文件的打开和关闭 3.1文件指针 3.2文件的打开和关闭 4.程序的顺序读写 4.2对比一组函数 5.文件的随机读写 5.1 fseek 5.2 ftell 5.3 rewind 6.文本文件和二进制文件 7.文件读取结束的…

2023年银行理财子公司研究报告

第一章 行业发展概况 1.1 行业概况 所谓“银行理财子公司”&#xff0c;其实就是由商业银行作为控股股东发起设立的&#xff0c;并经国务院银行业监督管理机构批准&#xff1b;主要从事理财业务&#xff1b;独立于母行&#xff0c;具有独立法人地位的非银行金融机构。像工商银…

Redission分布式锁

实现过程&#xff1a; 只要线程一加锁成功&#xff0c;就会启动一个 watch dog 看门狗&#xff0c;它一个后台线程&#xff0c; 会每隔 10 秒检查一下&#xff0c;如果线程 1 还持有锁&#xff0c;那么就会不断延长锁 key 生存时间。因此&#xff0c;Redisson 解决了锁过期释放…

Commitizen规范git提交代码

首先全局安装Commitizen&#xff0c;运行&#xff1a; npm install -g commitizen 然后在项目中开启终端&#xff0c;安装cz-customizable npm i cz-customizable --save-dev 然后在package.json中配置如下代码&#xff1a; "config": {"commitizen":…

虹科干货| 虹科Redis企业版数据库:告别游戏卡顿,让快乐加速!

“卡顿一分钟&#xff0c;玩家两行泪” 游戏已成为年轻人最主要的消遣娱乐方式之一&#xff0c;游戏卡顿给玩家带来糟糕游戏体验背后的原因是什么&#xff1f;数据存储与查询速度不够快&#xff01; 游戏开发领域&#xff0c;不仅拥有海量的数据&#xff0c;甚至还要做到实时…