Linux进程间通信之共享内存

📟作者主页:慢热的陕西人

🌴专栏链接:Linux

📣欢迎各位大佬👍点赞🔥关注🚓收藏,🍉留言

本博客主要内容讲解共享内存原理和相关接口的介绍,以及一个案例的展示

文章目录

  • system V共享内存
    • 1.共享内存的原理
    • 2.直接写代码--编写代码进行原理介绍
      • 2.1shmget接口的介绍
      • 2.2key值为什么需要用ftok生成
      • 2.3ftok接口
      • 2.3三个命令
      • 2.4shmat和shmdt
    • 3.通信测试
    • 4.代码

system V共享内存

共享内存属于是,操作系统单独为我们设计的进程间通信方式,最后慢慢演变成为了systemV的一种标准。它是属于文件系统之外的,但是它属于专门为了通信而设计的内核模块,我们称为systemV的IPC通信机制。

但是进程是具有独立性的,所以我们要实现进程间的通信,首先第一点就是要做到两个进程看到同一份资源

共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。

1.共享内存的原理

首先我们用一个例子来介绍:首先存在两个进程,我们画出对应的地址空间和页表。首先和我们之前的知识一样,进程内部的PCB内部有一个指针指向其对应的内存空间,然后地址空间和物理内存的映射关系存储在每个进程对应的页表中,唯独不同的是这时候页表映射到物理内存空间上却是同一份空间并且这个空间就是我们所谓的共享内存。

宏观控制共享内存的三个步骤:

  • ①创建
  • ②关联进程和取消关联
  • ③释放共享内存

image-20231130191729999

2.直接写代码–编写代码进行原理介绍

2.1shmget接口的介绍

  • 作用:用于申请system V的共享内存空间

  • 参数key :这个参数由我们的另一个接口生成:ftok:关于这个接口后面会介绍

  • 参数size :申请共享内存空间的大小

  • 参数 shmflg:

    这里我们介绍其中的两个:

    a. IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建,如果已经存在,获取已经存在的共享内存并返回。

    b. IPC_EXCL:不能单独使用,一般要配合IPC_CREAT来使用,含义是创建一个共享内存,如果共享内存不存在,就创建,如果已经存在,则立马出错返回 – 如果创建成功,对应的共享内存一定是最新的共享内存!

  • 返回值:成功返回对应的共享内存标识符,失败的话返回-1,并且errno被设置。

image-20231130195042509

2.2key值为什么需要用ftok生成

首先系统中可以用shm通信,是不是只能有一对进程使用共享内存呢?可以!->所以,在任何一个时刻,可能有多个共享内存在被用来进行通信->所以系统中一定会存在很多shm同时存在->OS要不要整体管理所有的共享内存呢?要!-> OS如何管理多个shm内存呢?先描述,再组织。所以共享内存,不是我们想象的那样,只要在内存中开辟空间即可,系统也要为了关了shm,构建对应的描述共享内存的结构体对象!!

共享内存 = 共享内存的内核数据结构(伪代码:struct shm) + 真正开辟的内存空间。

2.3ftok接口

  • 参数pathname:路径字符串
  • 参数proj_id:项目id

image-20231130213324725

在操作系统内部就是将一块块共享内存通过这种方式组织起来的,那么进程调用ftok通过pathname和proj_id来生成对应的key(也就是申请对应的共享内存空间),那么我们的进程B就可以通过对应的key来寻找对应的共享内存空间。从而实现进程间通信的本质:两个进程看到同一块资源,所以这个key本质是在内核中使用的。

image-20231130215301165

2.3三个命令

  • ipcs -m

    查看系统中存在的共享内存:

    image-20231204172852789

  • ipcrm -m shmid

    删除shmid所对应的共享内存,共享内存的声明周期不随进程,随OS。

    image-20231204173020261

  • shmctl

    这是一个函数接口:用来查看共享内存的一些相关属性,前提是我们执行这个进程的用户具有查看当前共享内存属性的权限,否则无法查看,一般sudo运行即可。或者创建时权限修改为066即可.

    image-20231204203451118

    代码:

        struct shmid_ds ds;
        int n = shmctl(shmid, IPC_STAT, &ds);
        if(n == -1)
        {
            cerr << errno << ":" << strerror(errno) << endl;
            exit(3);
        }
        cout << "prem:" << toHex(ds.shm_perm.__key) << endl;
        cout << "creater pid :" << ds.shm_cpid << ":" << getpid() << endl;
    

    运行效果:

    image-20231204203711729

2.4shmat和shmdt

这两个接口相关的是nttach参数,他表示的有多少个进程和当前的共享内存挂载的。

  • shmat

用于进程和共享内存产生关联,非常类似于malloc,返回一个void指针,一般我们把这个指针强制转换成char*;

image-20231205133313831

它内部也有一些参数,值得我们去学习和研究:

第一个参数我们已经很熟悉了共享内存描述符是个整数。

  • shmaddr:

    文档中的大概含义是,假如我们传入的是nullptr,那么操作系统就会自动帮我们分配一个未被使用过的合适的地址去链接这份共享内存

    如果shmaddr不为空,并且shmflg中指定了SHM_RND,则连接发生在地址上,并且该地址等于shmaddr,并四舍五入到最接近SHMLBA的倍数。否则shmadr必须是一个与页面对齐的地址,在这个地址上发生附加操作。

    image-20231205134727851

  • shmflg中的一些:

比如:SHM_RDONLY

image-20231205134520832

  • shmdt

    用于进程和对应的共享内存取消关联,相当于我们c语言当时学习的动态内存管理部分的free函数。

    image-20231205170541704

3.通信测试

那么好了总体来说我们要做四步:我们直接把这四步封装成一个类Init;

①获取key

②通过k获取shmid

③链接共享内存

④断开共享内存

#define SERVER 0x1
#define CLIENT 0x2

class Init
{
    //构造函数
public:
    Init(int T) :type(T)
    {
        //1.创建key
        key_t k = getKey();
        //2.获取shmid
        if(type == SERVER) shmid = creatShm(k, gsize);
        else shmid = getShm(k, gsize);
        //3.链接共享内存
        start = attachShm(shmid);
    }
    ~Init()
    {
        //1.切断共享内存
        dettachShm(start);
        //2.释放共享内存
        if(type == SERVER) delShm(shmid);
    }

    char* getstart()
    {
        return start;
    }

private:
    char* start; //共享内存的地址
    int type;  //辨别对象是server还是client
    int shmid; //共享内存标识符
};

运行结果:

在这里插入图片描述

4.代码

server.cc

#include"comm.hpp"

int main()
{
    Init server = Init(SERVER);

    //通信
    char* start = server.getstart();

    //输出从共享内存中读取到的信息
    int n = 0;
    while(n <= 30)
    {
        cout << "client->server #" << start << endl;
        sleep(1);
        n++; 
    }



    //拓展内容
    //1.client写完了,才通知让server读取,刚开始,一定先让client运行,一个管道
    //2.命名管道带进来
    //3.client写完了,才通知让server读取,读取完了,才让client进行写入,两个管道

    return 0;
}

client.cc

#include"comm.hpp"


int main()
{


    Init client = Init(CLIENT);

    //通信
    char* start = client.getstart();
    
    char c = 'A';

    while(c <= 'Z')
    {
        start[c - 'A'] = c;
        c++;
        start[c - 'A'] = '\0';
        sleep(1);
    }
 
    return 0;
}

comm.cpp

#ifndef COMM_HPP
#define COMM_HPP


#include<iostream>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cerrno>
#include<string.h>
#include<unistd.h>
#include<sys/stat.h>
#include<cassert>
#include<sys/types.h>

using namespace std;

//路径名称
#define PATHNAME "."
//项目id
#define PROJID 0x6666
//共享内存大小
#define gsize 4096



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

string toHex(key_t k)
{
    char buffer[64];
    snprintf(buffer, sizeof(buffer),"0x%x", k);
    return buffer;
}


static int creatShmHeaper(key_t k, int size, int flag)
{ 
    int shmid = shmget(k, size, flag);
    if(shmid == -1)
    {
        cerr << errno << ":" << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}

//创建共享内存
int creatShm(key_t k, size_t size)
{
    umask(0);
    return creatShmHeaper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}


//获取共享内存
int getShm(key_t k, size_t size)
{
    return creatShmHeaper(k, size, IPC_CREAT);
}

//删除shmid
void delShm(int shmid)
{
    int n = shmctl(shmid, IPC_RMID, nullptr);
    assert(n != -1);
    (void)n;
}

char* attachShm(int shmid)
{
    char* start = (char*)shmat(shmid, nullptr, 0);
    return start;
}

void dettachShm(char* start)
{
    int n = shmdt(start);
    assert(n != -1);
    (void)n;
}

#define SERVER 0x1
#define CLIENT 0x2

class Init
{
    //构造函数
public:
    Init(int T) :type(T)
    {
        //1.创建key
        key_t k = getKey();
        //2.获取shmid
        if(type == SERVER) shmid = creatShm(k, gsize);
        else shmid = getShm(k, gsize);
        //3.链接共享内存
        start = attachShm(shmid);
    }
    ~Init()
    {
        //1.切断共享内存
        dettachShm(start);
        //2.释放共享内存
        if(type == SERVER) delShm(shmid);
    }

    char* getstart()
    {
        return start;
    }

private:
    char* start; //共享内存的地址
    int type;  //辨别对象是server还是client
    int shmid; //共享内存标识符
};



#endif

到这本篇博客的内容就到此结束了。
如果觉得本篇博客内容对你有所帮助的话,可以点赞,收藏,顺便关注一下!
如果文章内容有错误,欢迎在评论区指正

在这里插入图片描述

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

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

相关文章

服务端测试开发必备技能:Mock测试

什么是mock测试 Mock 测试就是在测试活动中&#xff0c;对于某些不容易构造或者不容易获取的数据/场景&#xff0c;用一个Mock对象来创建以便测试的测试方法。 Mock测试常见场景 无法控制第三方系统接口的返回&#xff0c;返回的数据不满足要求依赖的接口还未开发完成&#…

[Redis]基础入门

Redis入门 一、初识Redis Redis是一种键值型的NoSql数据库。 其中键值型&#xff0c;是指Redis中存储的数据都是以key、value对的形式存储&#xff0c;而value的形式多种多样&#xff0c;可以是字符串、数值&#xff0c;甚至是json。 NoSql则是相对于传统关系型数据库而言&a…

【安卓12源码】WMS系列:addWindow 和 removeWindow流程

一、Window 的属性 Window的属性定义在WindowManager的内部类LayoutParams中&#xff0c;了解Window的属性能够更好的理解WMS的内部原理。Window的属性有很多种&#xff0c;与应用开发最密切的有三种&#xff0c;它们分别是Type(Window的类型)、Flag(Window的标志)和SoftInputM…

luceda ipkiss教程 39:修改版图text字体的粗细

通过修改font的line_width就可以修改版图中text字体的粗细&#xff1a; 代码如下&#xff1a; from si_fab import all as pdk import ipkiss3.all as i3class demo(i3.PCell):class Layout(i3.LayoutView):def _generate_elements(self, elems):elems i3.PolygonText(layer…

处理k8s中创建ingress失败

创建ingress&#xff1a; 如果在创建过程中出错了&#xff1a; 处理方法就是&#xff1a; kubectl get ValidatingWebhookConfiguration kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission 然后再次创建&#xff0c;发现可以&#xff1a;

AI封测需求强劲, AMD、英伟达等巨头将助推产业链增长 | 百能云芯

近期&#xff0c;超微&#xff08;AMD&#xff09;和英伟达&#xff08;NVIDIA&#xff09;相继发布了新一轮AI芯片&#xff0c;为封测产业链注入了新的活力。据业内人士透露&#xff0c;客户端对AI封测的需求愈发强劲&#xff0c;整体量能超过原先的估计&#xff0c;其中日月光…

【yolov8】与yolov5的区别及改进详解

图像识别技术在物联网、智能监控等领域广泛应用。而深度学习中的目标检测技术&#xff0c;能够帮助我们对图像中的目标进行识别&#xff0c;进而实现自动化控制。目前&#xff0c;Yolov8和Yolov5是目标检测领域热门的模型。 yolo目标检测原理yolov5详解yolov8yolov8结构图Conv模…

使用cpolar内网穿透实现内网SeaFile私有云盘的公网访问

文章目录 1. 前言2. SeaFile云盘设置2.1 Owncould的安装环境设置2.2 SeaFile下载安装2.3 SeaFile的配置 3. cpolar内网穿透3.1 Cpolar下载安装3.2 Cpolar的注册3.3 Cpolar云端设置3.4 Cpolar本地设置 4.公网访问测试5.结语 1. 前言 现在我们身边的只能设备越来越多&#xff0c…

解决cad找不到msvcr100.dll的有效方法,完美修复dll问题

在计算机使用过程中&#xff0c;我们经常会遇到一些错误提示&#xff0c;其中之一就是由于找不到msvcr100.dll文件而导致CAD软件无法正常运行的情况&#xff0c;系统无法找到所需的动态链接库文件。但是通过一些简单的解决方法&#xff0c;我们可以快速解决这个问题并继续我们的…

深度学习|keras编程基础

使用 tensorflow.keras 接口&#xff0c;组装神经网络层次&#xff0c;训练并预测 参考链接&#xff1a;https://blog.csdn.net/March_A/article/details/129240390?ops_request_misc&request_id&biz_id102&utm_termtensorflow.keras%20&utm_mediumdistribute…

IOday3作业

1> 使用文件IO完成对图像的读写操作 #include<myhead.h>int main(int argc, const char *argv[]) {//只读打开图片int fd-1;if((fd open("./R-C.bmp",O_RDWR))-1){perror("open");return -1;}//向后便宜两个字节找到大小的起始地址lseek(fd,2,S…

全网最新最全面的Appium自动化:Appium常用操作之混合应用webview页面操作--待补充!

上下文操作&#xff1a; 在appium中&#xff0c;对于混合应用&#xff0c;需要进行WebView页面和原生应用的切换 常用的方法如下&#xff1a; 1、context(self) / current_context(self)&#xff1a;返回当前会话的当前上下文&#xff0c;context可以理解为可进入的窗口。对于…

智慧物联可视化大屏赋能设备管理和城市运行

在智慧物联的时代&#xff0c;万物互联的网络正在构筑起一个智能化的世界。无论是家居设备、汽车、还是工业设备&#xff0c;都能通过互联网实现智能化管理和控制。随着物联网技术的发展&#xff0c;我们迅速步入了一个千姿百态的智慧时代。智慧物联逐渐渗透进我们的日常生活&a…

【Maven】依赖管理

1. 依赖管理 1.1 依赖配置 依赖&#xff1a;指当前项目运行所需要的jar包。一个项目中可以引入多个依赖。 依赖引入步骤&#xff1a;在pom.xml中编写标签&#xff0c;在标签中使用引入坐标&#xff0c;定义坐标的 groupId、artifactId、version&#xff0c;最后点击刷新&…

搬运工让你分分钟了解Web接口测试

01、什么是接口 百度说&#xff1a;接口泛指实体把自己提供给外界的一种抽象化物&#xff08;可以为另一实体&#xff09;&#xff0c;用以由内部操作分离出外部沟通方法&#xff0c;使其能被内部修改而不影响外界其他实体与其交互的方式 上面这句有点抽象&#xff0c;网上的…

线程池,及7大参数,4大拒绝策略详解

线程池&#xff0c;及7大参数&#xff0c;4大拒绝策略详解 1. 前言 1.1 什么是线程池&#xff1f; 线程池是一种利用池化技术思想来实现的线程管理技术&#xff0c;主要是为了复用线程、便利地管理线程和任务、并将线程的创建和任务的执行解耦开来。我们可以创建线程池来复用…

Upload-Labs-Linux

打开后看到了一堆题目&#xff0c;试着做做吧&#xff01; 一 上传一句话木马&#xff0c;告诉我们不能传带这些后缀的文件 传个图片马试试 发现传上去了&#xff0c;抓包修改一下 复制图片地址 验证一下&#xff0c;然后拿去蚁剑去连接 回到根目录下就看到flag了

在vscode下将ipynb文件转成markdown(.md文件)的方法

这里写自定义目录标题 写在最前面安装nbconvert工具vscode界面 or cmd终端 写在最前面 正常情况下&#xff0c;可以在vscode的ipynb界面点击上面的三个点&#xff0c;里面有export导出&#xff0c;可以选择直接输出html和pdf 但是没有markdown&#xff08;.md文件&#xff09;…

30个Python小游戏,小白练手,我都能玩一天【内附源码】

给大家带来30个 Python 小游戏&#xff0c;一定要收藏&#xff01; 文末获取完整代码 有手就行 1、吃金币 import os import cfg import sys import pygame import random from modules import *游戏初始化 def initGame():# 初始化pygame, 设置展示窗口pygame.init()screen…

解决方案:Mac 安装 pip

python3 --version 通过以下命令来下载pip&#xff1a; curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py curl命令允许您指定一个直接下载链接。使用-o选项来设置下载文件的名称。 通过运行以下命令安装下载的包&#xff1a; python3 get-pip.py