Linux_共享内存通信

目录

1、共享内存原理

2、申请共享内存

2.1 ftok 

2.2 测试shmget、ftok 

2.3 查看系统下的共享内存 

3、关联共享内存

3.1 测试shmat

4、释放共享内存 

4.1 测试shmctl 

5、实现共享内存通信 

6、共享内存的特性

结语 


前言:

        在Linux下,有一种进程间通信方式(IPC)名为共享内存,他是IPC中通信最快的方式(通信方式为全双工),因为他直接在物理内存上创建一块区域并且映射在进程的地址空间中,使得进程使用共享内存就如同直接使用动态申请的空间,因此通信过程少了内核的系统调用步骤,以至于相比于其他IPC模式速度更快,不过也正是因为在通信时不受内核管辖,导致共享内存不具备同步互斥机制,因此需要用户手动处理同步互斥问题。

        但是需要注意的是共享内存虽然使用起来如同动态空间,但是他的底层和动态空间不一样,动态空间具有独立性,只限于单个进程内部的访问,而共享内存允许多个无亲缘进程进行通信,因此他和动态空间是有区别的。

1、共享内存原理

        共享内存的目的就是为了进程间通信,而进程间通信的核心观念是让不同的进程看到同一份资源,所以共享内存必须在物理内存上开辟一块空间,并且映射到进程地址空间中的共享区,具体示意图如下:

        但是共享内存的申请和malloc申请是不一样的,因为共享内存要面向所有进程,要做到这一点就必须调用系统接口,所以要进行共享内存通信必须调用系统接口。

2、申请共享内存

        在物理内存上申请共享内存的接口是shmget,该接口介绍如下:

#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
//key是用户给这段共享内存设置的名字,一个key对应一个共享内存
//size表示申请共享内存的大小
//shmflg表示权限设置,常用的有IPC_CREAT和IPC_EXCL

//调用成功返回非负整数表示共享内存的标识码(给系统看的),失败返回-1

        着重介绍shmflg:

        1、当传递的是IPC_CREAT|IPC_EXCL,表示若以当前key值申请的共享内存不存在,则创建一个并返回新共享内存的标识码。若以当前key值申请的共享内存存在则返回-1,表示申请失败。

        2、当传递的是IPC_CREAT,表示若以当前key值申请的共享内存不存在,则创建一个并返回新共享内存的标识码。若以当前key值申请的共享内存存在则返回该共享内存的标识码。

        所以使用IPC_CREAT|IPC_EXCL可以判断一个key对应的共享内存是否存在,即key值是否被用过,当我们想用一段新的共享内存则可以使用IPC_CREAT|IPC_EXCL。

        key值的作用是判断两个进程的共享内存是否为同一个,两个进程所用的key一样说明他们共用同一个共享内存,反之则否,因此可以理解为key值是用户给一段共享内存起的名字,而shmget返回值是系统给这段共享内存起的名字。

2.1 ftok 

        shmget需要用到key值,key的类型虽然是key_t,但是也可以传一个int类型的值给到key,只不过这么做会导致潜在的重名风险,并且key的值需要程序员自己维护,于是系统提供了一个接口ftok,他像是一个算法,可以计算并返回一个key_t类型的值,该接口介绍如下:

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

key_t ftok(const char *pathname, int proj_id);
//接收一个路径和一个整形
//成功返回一个key_t类型的值,失败返回-1

         所以两个进程调用ftok时传参是一样的,那么这两个进程就会获得相同的key值,这样两个进程就能看到同一份资源了,也就完成了通信的前提。

2.2 测试shmget、ftok 

         先用代码测试上述接口,测试代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <iostream>

using namespace std;

int main()
{
    const char *pathname = "/home/zh";
    int proj_id = 12;
    int size = 4096;

    key_t key = ftok(pathname, proj_id);
    if (key < 0)
    {
        perror("ftok");
        return -1;
    }
    cout << "key值被成功创建,key:" << key << endl;

    int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL);
    if (shmid < 0)
    {
        perror("shmget");
        return -1;
    }
    cout<<"共享内存标识码被成功创建,shmid:"<<shmid<<endl;
    return 0;
}

        运行结果:

2.3 查看系统下的共享内存 

        共享内存不同于动态申请空间,动态空间的生命周期随进程。但是对于共享内存而言,若用户不主动释放共享内存,则共享内存会一直存活在系统中,他的生命周期随内核,即内核重启才会清理这些共享内存,在Linux下用指令ipcs -m查看当下系统的共享内存,测试如下:

        并且可以通过指令ipcrm -m shmid删除对应的shmid,测试如下:

3、关联共享内存

         上述接口shmget可以申请一块共享内存,但是申请到了不意味着就可以直接使用共享内存进行通信,要进行通信还要关联共享内存,关联共享内存的接口介绍如下:

#include <sys/types.h>
#include <sys/shm.h>

//关联共享内存
void *shmat(int shmid, const void *shmaddr, int shmflg);
//shmid表示要关联的共享内存标识码
//shmaddr若不为NULL且shmflg不为SHM_RND,表示将共享内存的地址附加到shmaddr处
//shmaddr若为NULL,则该函数的返回值作为共享内存的地址(通常都设为NULL)
//shmflg表示权限设置,通常设为0表示对共享内存可读可写
//调用成功返回指向共享内存的指针,失败返回值(void*)-1

//去关联
int shmdt(const void *shmaddr);
//让调用该函数的进程不再关联该共享内存
//shmaddr表示共享内存的地址

        总的来说,调用shmat关联共享内存后,会拿到一个执行该共享内存的指针,通过该指针就可以对共享内存进行读写操作。

3.1 测试shmat

        因为申请共享内存的代码后续会被重复使用,为了后续更好的测试,所以对申请共享内存的接口进行再一层封装,封装成sharemem.hpp文件,代码如下:

#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <iostream>
#include <unistd.h>

using namespace std;

const char *pathname = "/home/zh";
int proj_id = 12;
int size = 4096;

key_t getkey()//封装ftok
{
    key_t key = ftok(pathname, proj_id);
    if (key < 0)
    {
        perror("ftok");
        exit(-1);
    }
    cout << "key值被成功创建,key:" << key << endl;
    return key;
}

int getshm(int shmflg)//封装shmget
{
    key_t key = getkey();
    int shmid = shmget(key, size, shmflg);
    if (shmid < 0)
    {
        perror("shmget");
        exit(-1);
    }
    cout << "共享内存标识码被成功创建,shmid:" << shmid << endl;
    return shmid;
}

int creatnewshm()//只想用最新的共享内存来进程通信
{
    return getshm(IPC_CREAT|IPC_EXCL|0666);//为了能够观察到变化,所以要保证共享内存的权限
}

int getoldshm()//获取一个已经存在的共享内存进行通信
{
    return getshm(IPC_CREAT);
}

         后续的测试只需要包含该文件即可,测试shmat代码如下:

#include "sharemem.hpp"

int main()
{
    int shmid = creatnewshm();
    cout<<"申请共享内存成功"<<endl;
    sleep(2);//观察nattch的值

    char* poi = (char*)shmat(shmid,NULL,0);
    cout<<"关联共享内存成功"<<endl;
    sleep(2);//观察nattch的值

    return 0;
}

        运行结果:

        其中,右侧nattch表示当前有多少个进程在关联该共享内存,当一个进程关联某个共享内存后,该共享内存的nattch+1,并且当该进程结束后,对应的nattch会-1。当然也可以使用shmdt手动去关联。

4、释放共享内存 

        手动释放共享内存的接口是shmctl,该接口本质的功能是控制共享内存,只不过也有删除选项,具体介绍如下:

#include <sys/ipc.h>
#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
//shmid表示要释放的共享内存的标识码
//cmd表示该函数执行的具体任务,比如IPC_RMID表示删除任务
//buf表示指向共享内存数据结构的指针,若使用删除任务则该指针置为NULL即可

//调用成功返回0,失败返回-1

4.1 测试shmctl 

         测试shmctl的代码如下:

#include "sharemem.hpp"

int main()
{
    int shmid = creatnewshm();
    cout<<"申请共享内存成功"<<endl;
    sleep(2);//观察nattch的值

    char* poi = (char*)shmat(shmid,NULL,0);
    cout<<"关联共享内存成功"<<endl;
    sleep(2);//观察nattch的值

    shmdt(poi);
    cout<<"成功去关联共享内存"<<endl;
    sleep(2);//观察nattch的值

    shmctl(shmid,IPC_RMID,nullptr);
    cout<<"成功删除共享内存"<<endl;

    return 0;
}

        运行结果:

        从结果可以看到,无论是去关联测试还是删除共享内存,在右边的监控中都会显示对应的效果。 

5、实现共享内存通信 

         有了上述的接口以及sharemem.hpp文件,就可以实现两个进程的通信了,所以需要写一个客户端进程和一个服务器进程,其中服务器进程创建共享内存,让他们两都关联该共享内存,并且由客户端向服务器发送消息,服务器代码如下:

#include "sharemem.hpp"

int main()
{
    int shmid = creatnewshm();
    char *poi = (char *)shmat(shmid, nullptr, 0);
    cout << "关联共享内存成功" << endl;

    while (true)
    {
        cout<<"服务器接收:"<<poi<<endl;
        sleep(1);
    }
    
    shmdt(poi);
    shmctl(shmid,IPC_RMID,nullptr);

    return 0;
}

        客户端代码如下:

#include "sharemem.hpp"

int main()
{
    int shmid = getoldshm();
    char* poi = (char*)shmat(shmid,nullptr,0);
    cout<<"关联共享内存成功"<<endl;
    
    while (true)
    {
        string message;
        cout<<"客户端发送:";
        cin>>message;
        strcpy(poi,message.c_str());
    }
    shmdt(poi);
    return 0;
}

        测试结果:

        从结果可以发现,共享内存的通信本质就是对一个空间进行内存式的访问,无需调用read、write这些系统接口,直接用内存函数写入数据至内存对方就能够读取到内存里的数据。

6、共享内存的特性

        1、共享内存不需要调用系统接口实现进程间通信,只需要调用内存函数对内存进行读写即可实现进程间通信。

        2、共享内存本身没有同步互斥的概念,体现在上面的运行结果中读端会一直读内容(说明没有同步),并不会因为写端还未写而阻塞住。并且读端和写端可以同时访问共享内存(说明没有互斥)。

        3、共享内存在读写效率上更为高效,因为少了write和read这些步骤,即少了一层拷贝。 

结语 

        以上就是关于共享内存通信的讲解,共享内存作为IPC的其中一种方式,相对于其他通信方式,他有利有弊,在实际应用里先熟悉他的接口以及使用共享内存的步骤:申请共享内存(包括创建key值)、关联共享内存(shmat)、去关联(shmdt)、释放共享内存(shmctl) ,通过以上步骤可以实现完整的共享内存通信。

        最后如果本文有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!

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

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

相关文章

jenkins在使用pipeline时,为何没有方块形视图

项目场景&#xff1a; 安装完Jenkins时后&#xff0c;通过pipeline创建的项目任务。 问题描述 在立即构建后&#xff0c;没有显示每个阶段的视图。 原因分析&#xff1a; 原因是&#xff0c;刚安装的Jenkins&#xff0c;这个视图不是Jenkins自带的功能&#xff0c;而必须安装…

Cannot resolve symbol ‘log`

idea里的代码log变红色&#xff0c;是因为缺少Lombok插件。 安装lombok插件即可。安装完应用&#xff0c;重启软件就好了。 依次点击菜单栏中的 File → Settings&#xff08;Windows/Linux&#xff09; 或 IntelliJ IDEA → Preferences&#xff08;macOS&#xff09;。在设置…

设计模式探索:单例模式

1. 什么是单例模式? 定义: 单例模式是一种创建型设计模式,它确保一个类只有一个实例,并提供一种全局访问点以访问该实例。常见的场景包括身份证号码、政府等需要唯一实例的情况。 单例模式通常用于那些需要在应用程序中仅存在一个实例的情况,例如配置管理器、线程池、数据…

ret2syscall简单总结

主要是自己的简单的学习总结。 知识点 关于系统调用如何传递参数问题&#xff0c;即系统调用约定&#xff08;syscall&#xff0c;int 80h&#xff0c;svc&#xff09;_int 80h intel汇编用法-CSDN博客 ret2syscall的做题思路&#xff08;以32位程序为例&#xff09; - ZikH…

2024年【山东省安全员A证】考试试卷及山东省安全员A证考试试题

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 山东省安全员A证考试试卷根据新山东省安全员A证考试大纲要求&#xff0c;安全生产模拟考试一点通将山东省安全员A证模拟考试试题进行汇编&#xff0c;组成一套山东省安全员A证全真模拟考试试题&#xff0c;学员可通过…

react 项目中预防xss攻击的插件 dompurify

一、安装 $ yarn add dompurify $ yarn add --dev types/dompurify 二、使用 import DOMPurify from dompurify;// 1、处理&#xff1a; DOMPurify.sanitize(htmlContent)// 2、之后放进 dangerouslySetInnerHTML dangerouslySetInnerHTML{{ __html: cleanHTML }} 如&#…

Django自动生成Swagger接口文档 —— Python

1. 前言 当接口开发完成&#xff0c;紧接着需要编写接口文档。传统的接口文档通常都是使用Word或者一些接口文档管理平台进行编写&#xff0c;但此类接口文档维护更新比较麻烦&#xff0c;每次接口有变更&#xff0c;需要手动修改接口文档。在实际的工作中&#xff0c;经常会遇…

Docker:三、安装nginx与tomcat

&#x1f341;安装常见服务 &#x1f332;安装nginx &#x1f9ca;1、搜索镜像 Ⅰ.hub docker上查询&#xff1a;https://hub.docker.com/_/nginx Ⅱ. 命令查询&#xff1a;docker search nginx &#x1f9ca;2、下载镜像 命令&#xff1a;docker pull nginx &#x1f9c…

mmfewshot 框架概述、环境搭建与测试(一)

一、mmfewshot 框架概述 少样本学习的基本流程&#xff1a; 我们将为所有小样本学习任务引入一个简单的基线&#xff0c;以进一步说明小样本学习的工作原理。最明显的流程是微调。它通常包括两个步骤&#xff1a;在大规模数据集上训练模型&#xff0c;然后在小样本数据上进行微…

Matlab进阶绘图第62期—滑珠气泡图

在之前的文章中分享了滑珠散点图的绘制方法&#xff1a; 在此基础上&#xff0c;添加尺寸参数&#xff0c;通过散点的大小表示一个额外的特征&#xff0c;便是滑珠气泡图。 由于Matlab中没有现成的函数绘制滑珠气泡图&#xff0c;因此需要大家自行解决。 本文利用自己制作的B…

【C++】 解决 C++ 语言报错:Invalid Use of Incomplete Type

文章目录 引言 在 C 编程中&#xff0c;“Invalid Use of Incomplete Type” 是一种常见错误。此错误通常在程序试图使用未完全定义的类或结构时发生。这种错误不仅会导致编译失败&#xff0c;还可能导致程序行为不可预测。本文将详细探讨无效使用不完整类型的成因、检测方法及…

信号量(semaphore)

一、信号量简介 前面介绍的消息队列主要用于传输数据&#xff1a;任务与任务之间、任务与中断之间 在有些情况下&#xff0c;不需要传输数据&#xff0c;只需要传递状态即可 • 车开出停车位&#xff0c;你的车可以停进来了 • 课已经录制完成&#xff0c;你可以进行观看了 1.…

学习测4-缺陷管理略

缺陷 缺陷管理工具 jira 禅道 qc cq Bugfree缺陷的类型&#xff1a; 遗漏 missing 该做的没做 错误 error 该做的做错了 额外的实现 extra 不该做的做了软件缺陷的表现形式&#xff1a; 一.软件未实现需求规格说明书要求的功能 二.软件出现了需求规…

windows电脑如何运行python的定时任务

这里需要使用&#xff1a;windows系统设置-控制面板里的计划任务 1.打开计划任务之后&#xff0c;选择&#xff1a;创建基本任务 2.填写名称&#xff0c;这里根据自己具体的项目需求填写&#xff0c;然后点击下一步。 3.选择每日&#xff0c;再点击下一步 4.设置时间&…

【腾讯内推】腾讯2025校招/青云计划/社招——长期有效

及时跟进进度&#xff0c;保证不让简历石沉大海&#xff01; 涵盖NLP/CV/CG/ML/多模态/数据科学/多媒体等各方向! 定向匹配优质团队/竞争力薪酬/覆盖全球工作地点! 招聘对象: 本硕博:2024年1月-2025年12月毕业的同学 目前最热岗位: 技术研究-自然语言处理 技术研究-计算机视觉 …

【CV炼丹师勇闯力扣训练营 Day24:§7 回溯3】

CV炼丹师勇闯力扣训练营 代码随想录算法训练营第24天 93 复原IP地址 有效 IP 地址 正好由四个整数&#xff08;每个整数位于 0 到 255 之间组成&#xff0c;且不能含有前导 0&#xff09;&#xff0c;整数之间用 ‘.’ 分隔。 例如&#xff1a;“0.1.2.201” 和 “192.168.…

VBA提取word表格内容到excel

这是一段提取word表格中部分内容的vb代码。 Sub 提取word表格() mypath ThisWorkbook.Path & "\"myname Dir(mypath & "*.doc*")n 4 index of rowsRange("A1:F1") Array("课程代码", "课程名称", "专业&…

【Spring Boot】统一数据返回

目录 统一数据返回一. 概念二.实现统一数据返回2.1 重写responseAdvice方法2.2 重写beforeBodyWriter方法 三. 特殊类型-String的处理四. 全部代码 统一数据返回 一. 概念 其实统一数据返回是运用了AOP&#xff08;对某一类事情的集中处理&#xff09;的思维&#xff0c;简单…

【qt】如何获取网卡的信息?

网卡不只一种,有有线的,有无线的等等 我们用QNetworkInterface类的静态函数allInterfaces() 来获取所有的网卡 返回的是一个网卡的容器. 然后我们对每个网卡来获取其设备名称和硬件地址 可以通过静态函数humanReadableName() 来获取设备名称 可以通过静态函数**hardwareAddre…