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

news/2024/5/20 9:40:59

目录

一、什么是共享内存

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、获取keykey_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、获取keykey_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.mrgr.cn/p/20324017

相关文章

GitHub two-factor authentication开启教程

GitHub two-factor authentication开启教程问题描述 最近登录GitHub个人页面动不动就有一个提示框”...... two-factor authentication will be required for your account starting Jan 4, 2024 ......“,点击去看了一下原来是GitHub对所有的用户登录都要开启双重身份认证,…

Windows程序读取不了中文路径问题

解决win32接口无法解析中文路径的问题问题描述 今天调试发现win32接口GetFileAttributesW居然不支持中文路径,于是寻找解决方案,找了半天,尝试用boost的fileystem库发现能用,而且boost能跨平台! 不支持中文 win32接口获取文件属性,当传入参数带有中文字符时,它获取的属性…

栈和队列的4道面试题【详细解析】【代码实现】

栈和队列的面试题 1.有效的括号&#xff08;栈实现&#xff09; 题目&#xff1a; 有效的括号 给定一个只包括 (&#xff0c;)&#xff0c;{&#xff0c;}&#xff0c;[&#xff0c;] 的字符串 s &#xff0c;判断字符串是否有效。 有效字符串需满足&#xff1a; 左括号必…

紧跟生成式AI暴雨发布新时代推理服务器

近日&#xff0c;暴雨发布最新训推一体AI服务器&#xff0c;以大容量内存和灵活的高速互连选项满足各种AI应用场景&#xff0c;最大可能支持扩展插槽&#xff0c;从而大幅提升智能算力性能&#xff0c;以最优的性能和成本为企业的模型训练推理落地应用提供更好的通用算力。 AIG…

Java里的String使用

1.Java WinForm项目 public static void main(String[] args) {String testString"22";String testString2"1096";String testString3"22";Student studentnew Student();student.Age"22";Test(student.Age);Test2(student.Age); }pu…

【Qt QML】QLibrary加载共享库中的类

QLibrary是一个用于加载动态链接库&#xff08;或称为共享库&#xff09;的类。它提供了一种独立于平台的方式来访问库中的功能。 在QLibrary中&#xff0c;可以通过构造函数或setFileName()方法设置要加载的库文件名。当加载库文件时&#xff0c;QLibrary会搜索所有平台特定的…

trunk聚合

Eth-Trunk又叫以太网链路聚合Eth-Trunk,它通过将多条以太网物理链路捆绑在一起成为一条逻辑链路。达到增加链路带宽的目的。在实现增大带宽目的的同时,Eth-Trunk采用备份链路的机制,可以有效的提高设备之间链路的可靠性。每个聚合组唯一对应着一个逻辑接口,这个逻辑接口称之…

buuctf-pwn-[OGeek2019]babyrop

查看一下保护情况丢进ida里分析主函数调用了一个含有alarm的函数,这个函数会设置一个定时器,到时间自动退出程序 为了方便调试,我们直接patch掉这个函数 接着分析,主函数读入了一个随机数,并将其传入sub_804871F函数 sub_804871F函数读取输入,并检查输入的是否和随机数相…

Go 语言基础之指针、复合类型【数组、切片、指针、map、struct】

1、数组 特别需要注意的是&#xff1a;在 Go 语言中&#xff0c;数组长度也是数组类型的一部分&#xff01;所以尽管元素类型相同但是长度不同的两个数组&#xff0c;它们的类型并不相同。 1.1、数组的初始化 1.1.1、通过初始化列表{}来设置值 var arr [3]int // int类型的数…

团队作业4——项目冲刺 第 2篇 Scrum 冲刺博客

这个作业属于哪个课程 软件工程这个作业要求在哪里 团队作业4——项目冲刺这个作业的目标 团队完成任务的分配,明确团队每个人在接下来七天敏捷冲刺的目标其他参考文献这个作业所属团队 SuperNewCode团队成员 张楠 曾琳备 黄铭涛 张小宇 周广1.每日举行站立时会议2.燃尽图3.每…

WordPress MasterStudy LMS插件 SQL注入漏洞复现(CVE-2024-1512)

0x01 产品简介 WordPress和WordPress plugin都是WordPress基金会的产品。WordPress是一套使用PHP语言开发的博客平台。该平台支持在PHP和MySQL的服务器上架设个人博客网站。WordPress plugin是一个应用插件。 0x02 漏洞概述 WordPress Plugin MasterStudy LMS 3.2.5 版本及之…

【北京迅为】《iTOP-3588开发板快速烧写手册》-第8章 TF启动

RK3588是一款低功耗、高性能的处理器&#xff0c;适用于基于arm的PC和Edge计算设备、个人移动互联网设备等数字多媒体应用&#xff0c;RK3588支持8K视频编解码&#xff0c;内置GPU可以完全兼容OpenGLES 1.1、2.0和3.2。RK3588引入了新一代完全基于硬件的最大4800万像素ISP&…

vue案例

任务清单(单文件) <!DOCTYPE html> <html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>模板</title><sc…

【C++】滑动窗口:最大连续1的个数

1.题目 2.算法思路 其实在做这道题的时候并不需要真的把0翻转成1&#xff0c;只需要找到最长的子数组且该子数组中0的个数不大于K&#xff0c;就可以了&#xff01; 当然我们首先想到的是暴力穷举法&#xff1a; 找到所有符合题意的子数组&#xff0c;跳出最长的那个就可以了…

s7netplus二次应用

1. 安装这是个基于S7协议的开源协议2. 引用 using S7.Net;3. 创建PLC对象internal class s7net_lib{//idenfy basic link paramsprivate string plc_ip;private CpuType plc_type;private short plc_rack, plc_slot;public Plc my_plc;//constructor,含参构造函数 public s7ne…

MySQL-09.性能分析工具的使用

1.数据库服务器的优化步骤当遇到数据库调优问题时,思考的流程如下图。 整个流程划分成了观察(Show status)和行动(Action)两个部分。字母S的部分代表观察(会使用相应的分析工具),字母A代表的部分是行动(对应分析可以采取的行动)。上图,就是数据库调优的思路。如果发现执行SQ…

JENKINS 安装,学习运维从这里开始

Download and deployJenkins – an open source automation server which enables developers around the world to reliably build, test, and deploy their softwarehttps://www.jenkins.io/download/首先点击上面。下载Jenkins 为了学习&#xff0c;从windows开始&#x…

Tkinter组件:Checkbutton

Tkinter组件&#xff1a;Checkbutton Checkbutton&#xff08;多选按钮&#xff09;组件用于实现确定是否选择的按钮。Checkbutton 组件可以包含文本或图像&#xff0c;你可以将一个 Python 的函数或方法与之相关联&#xff0c;当按钮被按下时&#xff0c;对应的函数或方法将被…

英语学习笔记7——Are you a teacher?

Are you a teacher? 你是教师吗&#xff1f; 词汇 Vocabulary name /neɪm/ n. 名字&#xff0c;名声 英文名字构成&#xff1a; 名 字 姓      given name family name  也叫做&#xff1a;first name last name      例&#xff1a;Yanyan Gao 例句&#xff1…