Linux进程间通信:system V共享内存

目录

一、什么是共享内存

1.1创建共享内存

1.2释放共享内存

1.2.1shmctl

1.2.2shmat

1.2.3 shmdt

二、共享内存的实现及使用

2.1ShmClient

2.2Shm_Server

2.3Fifo.hpp

2.4Comm.hpp


一、什么是共享内存

标准系统V也叫system V的本地通信方式一般有三种:

1、共享内存

2、消息队列

3、信号量

而博主此片文章主要从共享内存方面去进行使用及讲解

1.1创建共享内存

共享内存的系统调用,第一个参数是共享内存在内核中唯一性的标识,第二个参数是所要创建共享内存的大小 ,第三个参数有两个选项:IPC_CRREAT和IPC_EXCL,有以下三种组合使用方法:

IPC_CRREAT:如果共享内存不存在,就创建它,如果共享内存已经存在,直接获取它

IPC_EXCL:不能单独使用,没有意义

IPC_CRREAT|IPC_EXCL:如果共享内存不存在就创建,如果存在就出错返回。

第三种方法可以保证如果创建的共享内存是成功的那么它一定是一个新的共享内存。

创建成功则会返回共享内存的标识符,失败返回-1,错误码被返回表示错误原因。

共享内存在内核中同时可以存在很多个。而OS管理多个共享内存依旧是六字真言:先描述再组织

所以对共享内存的管理就变成了对描述共享内存的结构体的管理。

而第一个参数key要具有唯一性,而这个key值不一定要我们自己去创建,可以去调用系统调用ftok这个算法生成一个key值。ftok第一个参数是用户传入一个const char*的字符串,第二个参数是一个int类型。所以我们可以通过ftok在两个进程中通过同样的pathname和id来让两个进行看到同一块共享内存。

文件操作,一个进程打开一个文件,进程退出的时候,这个被打开的文件就会被系统自动释放掉。文件的生命周期随进程但是注意:共享内存,如果进程结束,用户没有主动释放它,则其会一直存在。共享内存的生命周期随内核。

1.2释放共享内存

ipcs

ipcs可以查出三种资源:消息队列、共享内存、信号量。

perms则是权限

ipcs -m//查看系统中指定用户创建的共享内存

ipcrm -m shmid//删除shmid所对应的共享内存

以上都是指令级别的操作,而在代码中就需要调用系统接口来进行删除。

1.2.1shmctl

对共享内存进行设置、获取、删除三种功能。

shmid就是共享内存的id

cmd表示你想对其进行什么操作

buf则是含有共享内存熟悉的sturct结构体

获取用IPC_STAT可以获取struct shmid_ds中的各各种属性信息

设置用IPC_SET

删除用IPC_RMID

1.2.2shmat

at的意思就是attach关联的意思,因为共享内存是属于操作系统的,所以我们需要将共享内存挂接到当前进程地址空间的共享区,此时就需要用到shmat。

第一个参数表示要将哪一个共享内存贴到当前进程地址空间,第二个参数代表用户指明将shm挂接到哪里,所以第二个参数就是个地址(可以nullptr省略),第三个参数shmflag代表的是在进行挂接时的形式一般默认设置为0,保证可以读写。

如果挂接成功,会返回在进程地址空间中所挂接的虚拟地址。失败返回-1。

挂接完成后可以拿着返回的地址直接访问共享内存。 

1.2.3 shmdt

dt即detach去关联的意思,将共享内存从进程地址空间中删除。将共享内存的虚拟地址传进来,就可以进行进程地址空间和共享内存的去关联。

不管是指令级还是代码级的操作, 最后对共享内存进行控制,用的都是shmid,类似于操作文件时使用的fd。

二、共享内存的实现及使用

共享内存和管道不同,它不提供进程间协同的任何机制,这是共享内存的缺点,会引起数据不一致。但是共享内存是所有进程间通信中速度最快的。这是其优点 。

所以需要用户来进行共享内存协调机制的实现。

2.1ShmClient

#include "Comm.hpp"
#include "Fifo.hpp"

int main()
{
    //1、获取key
    key_t key=GetShmKeyOrDie();
    cout<<"key: "<<tosix(key)<<endl;
    
    //2、创建共享内存
    int shmid=GetShm(key,defaultsize);//创建一个全新的共享内存
    cout<<"shmid: "<<shmid<<endl;

    char* addr=(char*)ShmAttach(shmid);
    cout<<"Attach shm sucess,addr: "<<tosix((uint64_t)addr)<<endl;

    memset(addr,0,defaultsize);
    
    //以读方式打开管道
    Sync syn;
    syn.OpenReadOrDie();
    
    //进程间通信
    for(char c='A';c<='Z';c++)
    {
        addr[c-'A']=c;
        sleep(1);
        syn.Wakeup();
    }

    ShmDetach(addr);
    cout<<"Detach shm sucess,addr: "<<tosix((uint64_t)addr)<<endl;
    sleep(5);

    return 0;
}

2.2Shm_Server

#include "Comm.hpp"
#include "Fifo.hpp"

int main()
{
    //1、获取key
    key_t key=GetShmKeyOrDie();
    cout<<"key: "<<tosix(key)<<endl;

    //2、创建共享内存
    int shmid=CreateShm(key,defaultsize);//创建一个全新的共享内存
    cout<<"shmid: "<<shmid<<endl;

    //ShmDebug(shmid);

    //4、挂接共享内存
    char* addr=(char*)ShmAttach(shmid);
    cout<<"Attach shm sucess,addr: "<<tosix((uint64_t)addr)<<endl;

    //0.为了弥补进程间的协同机制,先引入管道
    Fifo fifo;
    Sync syn;
    syn.OpenReadOrDie();

    //进行进程间通信
    for(;;)
    {
        if(!syn.Wait()) break;
        cout<<"shm content: "<<addr<<endl;
    }


    ShmDetach(addr);
    cout<<"Detach shm sucess,addr: "<<tosix((uint64_t)addr)<<endl;


    //3、删除共享内存
    sleep(100);
    DeleteShm(shmid);

    return 0;
}

2.3Fifo.hpp

#ifndef __COMM_HPP__
#define __COMM_HPP__

//头文件
#include <cstring>
#include <string>
#include <cerrno>
#include <iostream>
#include<cassert>

//调用fifo所需的头文件+opean用到的头文件
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>//open
//调用unlink对管道进行析构的头文件
#include <unistd.h>

//define
#define Mode 0666
#define Path "./fifo"
using namespace std;

class Fifo
{
public:
    Fifo(const string &path=Path):_path(path)//创建有名管道
    {
        umask(0);
        int n=mkfifo(_path.c_str(),Mode);
        if(n==0)
        {
            cout<<"mkfifo sucess"<<endl;
        }
        else
        {
            cerr<<"mkfifo failed,errno: "<<errno<<",errstring: "<<strerror(errno)<<endl;
        }
    }
    ~Fifo()
    {
        int n=unlink(_path.c_str());//删除管道
        if(n==0)
        {
            cout<<"remove fifo file "<<_path<<"sucess"<<endl;
        }
        else
        {
            cerr<<"remove failed,errno: "<<errno<<",errstring: "<<strerror(errno)<<endl;
        }
    }
private:
    string _path;//有名管道的文件路径+文件名
};

class Sync
{
public:
    Sync()
    :rfd(-1)
    ,wfd(-1)
    {}
    void OpenReadOrDie()
    {
        rfd=open(Path,O_RDONLY);
        if(rfd<0) exit(1);
    }
    void OpenWriteOrDie()
    {
        wfd=open(Path,O_WRONLY);
        if(wfd<0) exit(1);
    }
    bool Wait()
    {
        bool ret=true;
        uint32_t c=0;
        ssize_t n=read(rfd,&c,sizeof(uint32_t));//读到数据才能接着往后走
        if(n==sizeof(uint32_t))
        {
            cout<<"server wakeup,begin read shm..."<<endl;
        } 
        else if(n<=0)
        {
            ret=false;
        }
        else
        {
            return false;
        }
        return ret;
    }
    void Wakeup()
    {
        uint32_t c=0;
        ssize_t n=write(wfd,&c,sizeof(uint32_t));
        assert(n==sizeof(uint32_t)); 
        cout<<"wakeup server..."<<endl;
    }
    ~Sync()
    {

    }
private:
    int rfd;
    int wfd;
};

#endif

2.4Comm.hpp

#pragma once

#include <unistd.h>
#include <iostream>
#include <cerrno>
#include <string>
#include <cstring>
#include <cstdlib>
//shmget 创建共享内存所需头文件
#include <sys/ipc.h>
#include <sys/shm.h>
//ftok 所需头文件
#include <sys/types.h>

using namespace std;

//创建一个共享内存

const char* pathname="/home/gaz";
const int proj_id=0x66;
const int defaultsize=4096;//单位是字节
//在内核中,共享内存大小是以4KB为基本单位的,只能用自己申请的大小,一般建议申请大小为N*4KB
//将标识符转换成16进制
string tosix(key_t k)
{ 
    char buffer[1024];
    snprintf(buffer,sizeof(buffer),"0x%x",k);
    return buffer;
}
//根据pathname和proj_id转换出一个key值
key_t GetShmKeyOrDie()
{
    key_t k=ftok(pathname,proj_id);
    if(k<0)
    {
        cerr<<"ftok error,errno: "<<errno<<",error string: "<<strerror(errno)<<endl;
        exit(1);
    }
    return k;
}

//创造或获取一个共享内存
int CreateShmorDie(key_t key,int size,int flag)
{
    int shmid=shmget(key,size,flag);
    if(shmid<0)
    {
        cerr<<"shmget error,errno: "<<errno<<",error string: "<<strerror(errno)<<endl;
        exit(2);
    }
    return shmid;
}
//对于服务端,创建一个全新的共享内存
int CreateShm(key_t key,int size)
{
    // IPC_CREAT: 不存在就创建,存在就获取
    // IPC_EXCL: 没有意义
    // IPC_CREAT | IPC_EXCL: 不存在就创建,存在就出错返回
    return CreateShmorDie(key,size,IPC_CREAT | IPC_EXCL | 0666);
}

//对于使用端,获取已经创建成功的共享内存
int GetShm(key_t key,int size)
{
    return CreateShmorDie(key,size,IPC_CREAT);
}

void DeleteShm(int shmid)
{
    int n=shmctl(shmid,IPC_RMID,nullptr);
    if(n<0)
    {
        cerr<<"shmctl error"<<endl;
    }
    else
    {
        cout<<"shmctl delete shm sucess,shmid: "<<shmid<<endl;
    }
}

void ShmDebug(int shmid)
{
    struct shmid_ds shmds;
    int n=shmctl(shmid,IPC_STAT,&shmds);//IPC_STAT获取shmid_ds中的各种属性信息
    if(n<0)
    {
        cerr<<"shmctl error"<<endl;
        return;
    }
    cout<<"shmds.shm_segsz: "<<shmds.shm_segsz<<endl;
    cout<<"shmds.shm_segsz: "<<shmds.shm_nattch<<endl;
    cout<<"shmds.shm_segsz: "<<shmds.shm_ctime<<endl;
}

void* ShmAttach(int shmid)//将共享内存和进程地址空间进行挂接
{
    void* addr=shmat(shmid,nullptr,0);
    if((long long int)addr==-1)//因为机器是64位的,int是4字节会引起精度损失
    {
        cerr<<"shmat error"<<endl;
        return nullptr;
    }
    return addr;
}

void ShmDetach(void* addr)//将共享内存和进程地址空间进行去关联
{
    int n=shmdt(addr);
    if(n<0)
    {
        cerr<<"shm error"<<endl;
    }
}

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

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

相关文章

行业新应用:电机驱动将成为机器人的动力核心

电机已经遍布当今社会人们生活的方方面面&#xff0c;不仅应用范围越来越广&#xff0c;更新换代的速度也日益加快。按照工作电源分类&#xff0c;可以将它划分为直流电机和交流电机两大类型。直流电机中&#xff0c;按照线圈类型分类&#xff0c;又可以分为有铁芯的电机、空心…

Redis 哨兵机制

文章目录 哨兵机制概念相关知识铺垫主从复制缺陷哨兵工作流程选举具体流程理解注意事项 哨兵机制概念 先抽象的理解&#xff0c;哨兵就像是监工&#xff0c;节点不干活了&#xff0c;就要有行动了。 Redis 的主从复制模式下&#xff0c;⼀旦主节点由于故障不能提供服务&#…

视频高效批量剪辑,一站式按顺序合并视频并添加精彩片头片尾,瞬间提升视频品质

视频已成为传递信息、展示创意的最佳方式。但你是否也曾因为繁琐的剪辑工作而头痛不已&#xff1f;别担心&#xff0c;今天我们就来聊聊如何轻松实现视频的高效批量剪辑&#xff0c;让你的作品按顺序完美合并&#xff0c;同时增添上令人眼前一亮的片头片尾&#xff0c;让你的宣…

【Python图像分类系列】建立CNN模型实现猫狗图像分类(案例+源码)

这是我的第275篇原创文章。 一、引言 基于CNN卷积神经网络在图像识别领域的应用&#xff1a;猫狗图像识别。主要内容包含&#xff1a; 数据创建和预处理 神经网络模型搭建 神经网络模型的训练和拟合 文中使用的深度学习框架是Keras。部分数据展示&#xff1a; 猫&#xf…

65-CPLD电路设计(安路为例)

视频链接 CPLD电路设计&#xff08;安路为例&#xff09;01_哔哩哔哩_bilibili CPLD电路设计&#xff08;以安路为例&#xff09; 浅谈板级电源设计的三种方法_哔哩哔哩_bilibili 参考【浅谈板级电源设计的三种方法】 FPGA板级硬件实战S1&#xff5e;7课 实战Power2-电…

【linux-IMX6ULL配置GPIO通用流程-以及时钟配置】

目录 1. GPIO模式控制流程1.1 LED、蜂鸣器、按键1.2 GPIO控制流程 2. 标准库的简要分析及实现&#xff1a;2.1 问题引入&#xff1a;2.2 代码实现&#xff1a; 3. 时钟配置总结&#xff1a;3.1 时钟树概要&#xff1a;3.2 IMX6ULL时钟概要及时钟树&#xff1a;3.3 IMX6ULL时钟配…

[C/C++] -- 代理模式

代理模式是一种结构型设计模式&#xff0c;允许一个对象&#xff08;代理&#xff09;控制另一个对象的访问。代理对象通常充当客户端和实际目标对象之间的中间人&#xff0c;从而控制对目标对象的访问&#xff0c;可以在访问前后进行一些额外的处理。 代理模式的优点包括&…

Python通过定义类实现增删改查(期末考试)

python高级编程期末测试 别看我挣的少&#xff0c;但是我省的多&#xff0c;昨天法拉利又省下两百多万。 一、通过创建自己类来实现增删改查 我们已经利用模型实现单表的增删改查了 现在 我们不想使用模型来操作数据库 我们可以自己定义模型 那么 如何通过自己创建的类实现增…

商标注册证下证的前后时间的注意!

近日下了6个商标注册证&#xff0c;商标初审公告是3个月时间&#xff0c;如果没人提出异议&#xff0c;公告结束后1个月内基本上都可以拿到商标注册证电子版&#xff0c;没有纸制版的&#xff0c;凡是邮寄到付签收纸制商标注册证的基本都是骗。 对商标提出异议的主体平常会在公…

2024年03月 C/C++(二级)真题解析#中国电子学会#全国青少年软件编程等级考试

C/C编程&#xff08;1~8级&#xff09;全部真题・点这里 第1题&#xff1a;满足条件的数的累加2 现有n个整数&#xff0c;将其中个位数为k的数进行累加求和 输入 第一行一个整数n。第二行n个非负整数&#xff0c;以空格分割&#xff0c;每个数不大于100000。第三行一个整数k。 …

uniapp——列表分享当前话题(一个页面多个分享)

案例 分享的时候弹出对应的标题和默认第一张图片 代码 <view v-for"(item,index) in list" :key"index"><button open-type"share" :id"index" click.stop"()>{}"><image src"/static/images/cir…

多客陪玩系统源码APP小程序H5陪玩开发伴游源码游戏陪玩平台源码陪玩平台开发约单源码线下陪玩接单平台app小程序H5源码游戏陪玩app小程序H5开发

出售成品陪玩app小程序H5源码&#xff0c;免费搭建部署和售后服务&#xff0c;并提供源码二开、定制开发等相关服务。 一、陪玩app源码的功能介绍 1、语音聊天: 陪玩app小程序H5源码用户随时创建语音聊天室&#xff0c;实现多用户上麦功能&#xff0c;提高互动聊天体验。 2、游…

Apache SeaTunnel 4月回顾:明星贡献者与技术突破

各位热爱 SeaTunnel 的小伙伴们&#xff0c;SeaTunnel 社区 4 月份月报来啦&#xff01;这里将记录 SeaTunnel 社区每月的重要更新&#xff0c;欢迎关注&#xff01; 月度 Merge 之星 感谢以下小伙伴 4 月为 Apache SeaTunnel 做的精彩贡献&#xff08;排名不分先后&#xff…

02-单片机商业项目编程,从零搭建低功耗系统设计

一、本文内容 上一节《01-单片机商业项目编程&#xff0c;从零搭建低功耗系统设计-CSDN博客》已经对事件驱动原理有个基本了解&#xff0c;本节主要就是如何将事件写的更规范&#xff0c;而不是用t_flag这样的标记&#xff0c;写多了可读性也不强&#xff1b;本节结尾总结将提出…

蓝牙 | 软件:Git管理高通的ChipCode项目

哈喽大家好&#xff0c;最近发现大家在高通chipcode网站上下载不了代码&#xff0c;小编一直使用git的方式获取新版本代码&#xff0c;没有遇到什么阻碍。于是小编到新主机上尝试下载代码的压缩包和git代码&#xff0c;都遇到了问题。由于压缩包是高通自己处理卡住了&#xff0…

【Mac】LiveWallpaper(超高清4K动态壁纸) 安装教程

软件介绍 今天给大家介绍的一款软件叫Live Wallpaper & Themes 4K Pro&#xff0c;这是一款超高清4K动态壁纸应用程序。 Live Wallpaper & Themes 4K Pro是一款提供高品质动态壁纸和主题的应用程序。以下是它的一些主要特点和功能&#xff1a; 1.高清和4K动态壁纸&a…

Shell变成规范与变量

目录 1. Shell脚本 1.1 Shell脚本概述 1.2 Shell的作用 1.3 Shell脚本的构成 2. 重定向与管道操作 2.1 交互式硬件设备 ​ 2.2 重定向操作 3. shell变量 3.1 自定义变量 3.2 变量的作用范围​编辑 3.3 整数变量的运算 4. 环境变量 4.1 特殊的Shell变量 4.2 只读变…

二总线,替代传统485总线通讯,主动上报方案简易实现方法

二总线通信设计专栏 《二总线&#xff0c;替代传统485总线通讯&#xff0c;选型及应用-CSDN博客》《二总线&#xff0c;替代传统485总线通讯&#xff0c;低成本直流载波方案实现及原理-CSDN博客》《二总线&#xff0c;替代传统485总线通讯&#xff0c;调试避坑指南之最大的电流…
最新文章