[Linux]----进程间通信之共享内存

news2025/1/21 12:20:57

文章目录

  • 前言
  • 一、system V共享内存
    • 原理
  • 二、创建共享内存
    • 关联和去关联共享内存
    • 进行通信
  • 三、基于管道进行共享内存通信
  • 四、临界资源
  • 五、信号量
  • 总结


前言

基于上篇我们利用管道进行进程间通信的使用和实现,本篇将带大家通过共享内存进行进程间通信!


正文开始

一、system V共享内存

首先回顾上篇的内容

进程进通信的前提是,先让不同的进程,看到同一份资源!

原理

在这里插入图片描述
系统提供给了我们如下接口
1.创建共享内存------删除共享内存(OS完成)
2.关联共享内存------去关联共享内存(进程完成)
在这里插入图片描述
上面函数的第二个参数建议设置成为页(4KB)的整数倍!
在这里插入图片描述
那么问题来了
1.共享内存存在哪里呢?

存在内核中—内核会给我们维护共享内存的结构!!—所以内核中一定存在描述共享内存的内核数据结构—struct shmid_ds{};
在这里插入图片描述

共享内存要被管理–>struct shmid_ds{}–>struct ipc_perm—>key(shmget)—>共享资源的唯一值。

我怎么知道,这个共享内存到底存在还是不存在呢?

先有方法表示共享内存的唯一性!!(key)–>一般是由用户提供的!

共享内存,在内核中,让不同的进程看到同一份共享内存的做法是:让他们拥有同一个key即可!!!

类比于命名管道进行理解

命名管道–>约定好使用同一个文件,来进行通信的!!

共享内存–>约定好使用同一个唯一key,来进行通信的!!

那么如何生成我们的key呢,系统给我们提供了接口!!

在这里插入图片描述

二、创建共享内存

//Comm.hpp

#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<cstring>
#include<cerrno>
#include<cassert>
#include<cstdlib>
#include<sys/shm.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string>
#include"Log.hpp"

#define PATH_NAME "/home/hulu"
#define PROJ_ID 0x14

key_t CreateKey() 
{
    key_t key=ftok(PATH_NAME,PROJ_ID);
    if(key<0)
    {
        std::cerr<<"ftok "<<strerror(errno)<<std::endl;
        exit(1);
    }
    return key;
}

//Log.h

#pragma once

#include<iostream>
#include<ctime>

std::ostream& Log()
{
   std::cout << "For Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | ";
    return std::cout;
}

//IpcShmSer.cc

#include"Comm.hpp"
#include"Log.hpp"
#include<unistd.h>

using namespace std;
int main()
{

    key_t key=CreateKey();
    Log()<<"key: "<<key<< "\n";
    Log()<<"create share memory  begin!\n";
    int shmid=shmget(key,MEM_SIZE,IPC_CREAT | IPC_EXCL|0666);
    if(shmid<0)
    {
        Log()<<"shmget: "<<strerror(errno)<< "\n";
        return 2;
    }
    Log()<<"create shm success,shmid: "<<shmid<< "\n";
    return 0;
}

在这里插入图片描述

当我们运行完毕创建全新的共享内存的代码后(进程退出),但是第二(n)次的时候,该代码无法运行,告诉我们file存在!—>我要创建的共享内存是存在的!—>system V下的共享内存,生命周期是随内核的!!—>如果不显式的删除,只能通过Kernel(OS)重启解决!

那么我怎么知道有哪些IPC资源呢?

ipcs -m

在这里插入图片描述

这个就是我们刚刚创建的共享内存!!

如何显式的删除呢?

ipcrm -m shmid

在这里插入图片描述
还可以使用系统接口删除
在这里插入图片描述在这里插入图片描述
在这里插入图片描述

int main()
{

    key_t key=CreateKey();
    Log()<<"key: "<<key<< "\n";

    Log()<<"create share memory  begin!\n";

    int shmid=shmget(key,MEM_SIZE,IPC_CREAT | IPC_EXCL|0666);
    if(shmid<0)
    {
        Log()<<"shmget: "<<strerror(errno)<< "\n";
        return 2;
    }
    Log()<<"create shm success,shmid: "<<shmid<< "\n";
     //删除
     shmctl(shmid,IPC_RMID,nullptr);
     Log()<<"delete shm : "<<shmid<<"success\n";
}

在这里插入图片描述
这个权限默认是0,证明其他进程无法访问,我在这里设置为666,以便我们接下来的实验!

关联和去关联共享内存

共享内存虽然是这个进程创建的,但是他不属于这个进程!

需要我们调用系统接口将他们关联起来!

在这里插入图片描述
你怎么使用malloc的空间,你就怎么使用共享内存的空间!

int main()
{

    key_t key=CreateKey();
    Log()<<"key: "<<key<< "\n";

    Log()<<"create share memory  begin!\n";

    int shmid=shmget(key,MEM_SIZE,IPC_CREAT | IPC_EXCL|0666);
    if(shmid<0)
    {
        Log()<<"shmget: "<<strerror(errno)<< "\n";
        return 2;
    }
    Log()<<"create shm success,shmid: "<<shmid<< "\n";
    sleep(10);
    //使用
    //1.将共享内存和自己的进程产生关联attach
    char* str=(char*)shmat(shmid,nullptr,0);
    Log()<<"attach shm: "<<shmid<<"success\n";
    sleep(10);


    //2.去关联
    shmdt(str);
    Log()<<"detach shm: "<<shmid<<"success\n";


    // //删除
     shmctl(shmid,IPC_RMID,nullptr);
     Log()<<"delete shm : "<<shmid<<"success\n";
}

在这里插入图片描述

进行通信

//IpcShmSer.cc

int main()
{

    key_t key=CreateKey();
    Log()<<"key: "<<key<< "\n";

    Log()<<"create share memory  begin!\n";

    int shmid=shmget(key,MEM_SIZE,IPC_CREAT | IPC_EXCL|0666);
    if(shmid<0)
    {
        Log()<<"shmget: "<<strerror(errno)<< "\n";
        return 2;
    }
    Log()<<"create shm success,shmid: "<<shmid<< "\n";
    //使用
    //1.将共享内存和自己的进程产生关联attach
    char* str=(char*)shmat(shmid,nullptr,0);
    Log()<<"attach shm: "<<shmid<<"success\n";
    while(true)
    {
        //让读端进行等待
        printf("%s\n",str);
        sleep(1);
    }
    //2.去关联
    shmdt(str);
    Log()<<"detach shm: "<<shmid<<"success\n";


    // //删除
     shmctl(shmid,IPC_RMID,nullptr);
     Log()<<"delete shm : "<<shmid<<"success\n";
}

//IpcShmCli.cc

using namespace std;
int main()
{
    key_t key = CreateKey();
    Log() << "key: " << key << "\n";

    //获取共享内存
    int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
    if (shmid < 0)
    {
        Log() << "shmget: " << strerror(errno) << "\n";
        return 2;
    }
    //挂接
    char *str = (char *)shmat(shmid, nullptr, 0);

    //使用
    //竟然没有使用任何的系统调用接口
    int cnt=0;
    while(cnt<=26)
    {
        str[cnt]='A'+cnt;
        cnt++;
        str[cnt]='\0';
        sleep(1);
    }
    //去关联
    shmdt(str);
    return 0;
}

在这里插入图片描述
我们把共享内存实际上是映射到了我们的进程地址空间的用户空间了(堆->栈之间),对每一个进程而言挂接到自己上下文中的共享内存,属于自己的空间,类似于堆空间和栈空间,可以被用户直接使用!!

共享内存,因为它自身的特性,他没有任何访问控制!共享内存被双方直接看到,属于双方的用户空间,但是不安全!

共享内存是所有进程间通信,速度最快的!

三、基于管道进行共享内存通信

//Comm.hpp

#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<cstring>
#include<cerrno>
#include<cassert>
#include<cstdlib>
#include<sys/shm.h>
#include<unistd.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<string>
#include"Log.hpp"

#define PATH_NAME "/home/hulu"
#define PROJ_ID 0x14

key_t CreateKey() 
{
    key_t key=ftok(PATH_NAME,PROJ_ID);
    if(key<0)
    {
        std::cerr<<"ftok "<<strerror(errno)<<std::endl;
        exit(1);
    }
    return key;
}
void CreateFifo()
{
    umask(0);
    if(mkfifo(FIFO_FILE,0666)!=0)
    {
        Log()<<strerror(errno)<<"\n";
        exit(2);
    }
}

#define READER O_RDONLY
#define WRITER O_WRONLY

int Open(const std::string& filename,int flags)
{
    return open(filename.c_str(),flags);
}

int Wait(int fd)
{
    uint32_t values=0;
    ssize_t s=read(fd,&values,sizeof(values));
    return s;
}

int Signal(int fd)
{
    uint32_t cmd=1;
    write(fd,&cmd,sizeof(cmd));
}

int Close(int fd,const std::string filename)
{
    close(fd);
    unlink(filename.c_str());
}

//Log.h

#pragma once

#include<iostream>
#include<ctime>

std::ostream& Log()
{
   std::cout << "For Debug |" << " timestamp: " << (uint64_t)time(nullptr) << " | ";
    return std::cout;
}

//IpcShmSer.cc

#include"Comm.hpp"
#include"Log.hpp"
#include<unistd.h>

using namespace std;

#define FIFO_FILE ".fifo"
#define READER O_RDONLY
#define WRITER O_WRONLY

int main()
{

    key_t key=CreateKey();
    Log()<<"key: "<<key<< "\n";

    Log()<<"create share memory  begin!\n";

    int shmid=shmget(key,MEM_SIZE,IPC_CREAT | IPC_EXCL|0666);
    if(shmid<0)
    {
        Log()<<"shmget: "<<strerror(errno)<< "\n";
        return 2;
    }
    Log()<<"create shm success,shmid: "<<shmid<< "\n";

     CreateFifo();
     int fd=Open(FIFO_FILE,READER);
     assert(fd>=0);
    //使用
    //1.将共享内存和自己的进程产生关联attach
    char* str=(char*)shmat(shmid,nullptr,0);
    Log()<<"attach shm: "<<shmid<<"success\n";
    while(true)
    {
        //让读端进行等待
        if(Wait(fd)<=0) break;
        printf("%s\n",str);
        sleep(1);
    }

    //2.去关联
    shmdt(str);
    Log()<<"detach shm: "<<shmid<<"success\n";

    // //删除
     shmctl(shmid,IPC_RMID,nullptr);
     Log()<<"delete shm : "<<shmid<<"success\n";

    Close(fd,FIFO_FILE);
}

//IpcShmCli.cc

#include "Comm.hpp"
#include "Log.hpp"
#include <cstdio>
#include <unistd.h>

#define FIFO_FILE ".fifo"
#define READER O_RDONLY
#define WRITER O_WRONLY

using namespace std;
int main()
{
    int fd=Open(FIFO_FILE,WRITER);
    key_t key = CreateKey();
    Log() << "key: " << key << "\n";

    //获取共享内存
    int shmid = shmget(key, MEM_SIZE, IPC_CREAT);
    if (shmid < 0)
    {
        Log() << "shmget: " << strerror(errno) << "\n";
        return 2;
    }

    //挂接
    char *str = (char *)shmat(shmid, nullptr, 0);
    while(true)
    {
        printf("Please Enter#");
        fflush(stdout);
        ssize_t s=read(0,str,MEM_SIZE);
        if(s>0)
        {
            str[s]='\0';
        }
        Signal(fd);
    }

    //去关联
    shmdt(str);

    return 0;
}

在这里插入图片描述

四、临界资源

能被多个进程同时看到的资源----临界资源

如果没有对临界资源进行任何保护,对于临界资源的访问,双方进程在进行访问的时候,就都是乱序的,可能会因为读写交叉而导致各种的乱码和废弃数据访问控制方面的问题!!!

与之前fork()创建子进程后,父子进程打印数据是乱序的,缺乏访问控制!

对多个进程而言,访问临界资源的代码—临界区!

我的进程代码中,有大量的代码。但是只有一部分代码,会访问临界区!

我们把一件事情,要不没做,要么做完了(没有中间状态)----原子性

任何时刻,只允许一个进程访问临界资源—互斥

五、信号量

  1. 什么是信号量?
    信号量本质就是一个计数器!!!

当信号量为1的时候表示的就是互斥特性!

互斥信号量----二元信号量

常规信号量—多元信号量

每一个进程要访问临界资源,必须先申请信号量–>就要求每一个进程必须先看到这个信号量—>所以信号量本身也是临界资源!!!—>信号量对应的操作是原子的!

信号量对应的操作是PV操作!!
sem:–; 申请资源:P
sem:++;释放资源 V

共享内存没有访问控制,我们可以通过信号量进行对资源的保护!


总结

临界资源和信号量的主要内容会在线程部分着重带大家理解!
这里是带大家浅浅的认识一下!有个了解即可!

(本章完!)

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

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

相关文章

R9.8-9.8-9.8-9.8A_特点

R9.8-9.8-9.8-9.8A_特点 R9.8-9.8-9.8-9.8A_特点哈威柱塞泵主要特点是高自吸转速&#xff0c;工作效率高&#xff0c;结构紧凑&#xff0c;工作压力高等。该泵可为工程机械、铲雪车、路堤割草机、柴油叉车、自驱式工作平台和农业机械中的执行元件等提供压力油。 哈威液压泵R9.…

LIRA: Learnable, Imperceptible and Robust Backdoor Attacks 论文笔记

论文信息 论文名称LIRA: Learnable, Imperceptible and Robust Backdoor Attacks作者Khoa Doan(Baidu Research)会议/出版社ICCV 2021pdf&#x1f4c4;在线pdf代码&#x1f4bb;pytorch其他该作者还有一篇攻击的论文&#xff0c;在线pdf 介绍 本文提出了一种新的攻击框架 LIR…

技术中台的定义、范围与内容

【摘要】中台不能算是一个新的概念,只不过把它从一个单一系统扩展到了企业内部所有系统和组织级(企业架构级)的概念。那么时下“中台”的本质究竟是什么?技术中台又该如何理解? 前台、中台、后台的概念早就有之,只不过不同的场景下有不同的内涵。前中后台是从应用系统架…

T293037 [传智杯 #5 练习赛] 白色旅人

题目描述 有一个物品队列 \frak BB&#xff0c;初始时为空。现在共有三种操作。每个操作会给定三个整数 \mathrm{op},x,yop,x,y&#xff0c;其中 \mathrm{op}op 表示操作种类&#xff0c;x,yx,y 是操作的参数。操作分为如下三种&#xff1a; 11&#xff1a;向 \frak BB 尾部添…

React 简书项目实战【3】实现搜索框动画

React 简书项目实战【3】实现搜索框动画 添加判断变量 header/index.js 核心代码 ... class Header extends Component {constructor(props) {super(props);this.state {focused: true}}render() { ...添加classname header/index.js 核心代码 搜索框和放大镜图标 <…

手把手教你如何用界面组件DevExpress WPF应用一个模板主题

DevExpress WPF拥有120个控件和库&#xff0c;将帮助您交付满足甚至超出企业需求的高性能业务应用程序。通过DevExpress WPF能创建有着强大互动功能的XAML基础应用程序&#xff0c;这些应用程序专注于当代客户的需求和构建未来新一代支持触摸的解决方案。 DevExpress WPF组件包…

VR失重太空舱游乐设备|航空航天VR体验|VR航天航空体验馆

普乐蛙推出了十几种不同类型的VR航天航空体验设备&#xff0c;VR创意内容丰富好玩&#xff0c;360全景还原航空各个场景&#xff0c;体验享受航空中的失重、飞行、旋转等刺 激与乐趣;模拟月球、太空、空间站等丰富航天场景&#xff0c;近距离体验航天科幻十足的魅力;通过沉浸式…

vue(移动端)使用高德地图实现精准定位

目录 效果图 前提准备 代码展示 效果图 两个页面 页面一&#xff08;粗略定位&#xff09; 点击城市进入页面二 &#xff08;粗略定位&#xff09;&#xff0c;搜索框输入具体位置&#xff08;以大连理工大学为例&#xff09; 点击大连理工大学&#xff0c;回到页面一&…

[附源码]java毕业设计校园淘宝节系统

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

MongoDB Journaling工作原理

文章目录 小结当 mongod 进程启动后,首先将数据文件映射到 shared 视图中,假如数据文件的大小为 4000 个字节,它会将此大小的数据文件映射到内存中,地址可能为 1000000~1004000。如果直接读取地址为1000060的内存,我们将得到数据文件中第60个字节处的内容。有一点要注意,…

ElementUI的Form表单使用slot-scope=“scope“获取当前表格行数据实现数据回显、修改表单操作

在写项目时&#xff0c;老师通过向后端发请求获得表格原来的数据来填充修改表单里的数据。 这是表格&#xff1a; 这是点击修改按钮后显示出来的修改表单&#xff1a; 但本地里都已经有这些数据了&#xff0c;就没必要再发一次请求&#xff0c;徒增服务器压力。 准备 可是…

Java基础之《netty(3)—NIO之Buffer》

一、Buffer基本介绍 1、缓冲区&#xff08;Buffer&#xff09; 缓冲区本质上是一个可以读写数据的内存块&#xff0c;可以理解成是一个容器对象&#xff08;数组&#xff09;。该对象提供了一组方法&#xff0c;可以更轻松的使用内存块。缓冲区对象内置了一些机制&#xff0c;…

黑盒子问题

一 问题描述 黑盒子代表一个原始数据库&#xff0c;存储一个整数数组和一个特殊的 i 变量。最初的时刻&#xff0c;黑盒子是空的&#xff0c;i0&#xff0c;黑盒子处理一系列命令&#xff08;事务&#xff09;。有两种类型的事务。 ① ADD(x)&#xff0c;将元素 x 放入黑盒子…

按用户导出数据到asm磁盘组,并复制到另一个集群的asm

1.创建asm导出数据目录 sql>select name,total_mb,free_mb from v$asm_diskgroup; 确认集群asm磁盘组环境 asmcmd>cd DGDSDB asmcmd>mkdir dpbak asmcmd>ls -l sql>conn / as sysdba create directory expdp_asm_dir as DGDSDB/dpbak; create directory expdp_l…

堆-c语言实现

1. 树是什么&#xff1f; 树是一种非线性的数据结构&#xff0c;它是由n&#xff08;n>0&#xff09;个有限结点组成一个具有层次关系的集合 注意&#xff1a;根结点没有前驱结点&#xff1b;每棵子树的跟结点有且只有一个前驱结点&#xff0c;可能有0个或者多个后继结点&am…

数学建模学习(108):帮助小白快速实现批量机器学习建模训练和批量的数据可视化

本文主要针对机器学习知识薄弱,不太擅长搭建模型同学使用。 本文主要是讲解可以快速轻松实现可视化、数据预处理、批量模型构建。帮助大家轻松做到可视化和建模。特别是机器学习不扎实同学。 数据科学模型开发管道涉及各种组件,包括数据收集、数据处理、探索性数据分析、建模…

unity il2cpp打包安卓打包崩溃原因Unity2020.3 il2cpp.so丢失

Unity2020.3 il2cpp.so 问题&#xff1a;升级unity2020.3后&#xff0c;使用il2cpp方式打Android包&#xff0c;在手机上启动会崩溃&#xff0c;追查崩溃原因是il2cpp.so not found。 解决过程&#xff1a; il2cpp.so没有&#xff1f;一脸懵逼&#xff01;记得以前在调用Bui…

面试八股 | 计算机网络 | TCP三次握手

CP三次握手和四次挥手是面试题的热门考点&#xff0c;它们分别对应TCP的连接和释放过程&#xff0c;今天我们先来认识一下TCP三次握手过程&#xff0c;以及是否可以使用“两报文握手”建立连接。1、TCP是什么&#xff1f; TCP是面向连接的协议&#xff0c;它基于运输连接来传送…

【附源码】计算机毕业设计JAVA养老机构系统

【附源码】计算机毕业设计JAVA养老机构系统 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; JAVA mybati…

国际物流和跨境电商物流的区别

跨境物流是将货物通过海、陆、空运输从一个国家运输到另一个国家或地区&#xff0c;最终完成交易的目的。国际物流是实现两地对物资进行物理移动的一项国际商品交易或交流活动&#xff0c;从而完成国际商品交易的终目的。二者说法不同而已&#xff0c;本质上两者并没有太大的区…