【linux】共享内存

简介: 【linux】共享内存

1.原理

2.相关函数以及相关指令

1.shmget//创建共享内存

#include <sys/ipc.h>
  #include <sys/shm.h>
       int shmget(key_t key, size_t size, int shmflg);

key为用户给操作系统来标识共享内存的标识符,方便另一个进程来找到共享内存

size为共享内存大小

shmflg 创建共享内存的方式,位图形式

返回shmid操作系统给用户的对应共享内存的操作符,用来对共享内存进行操作,比如说要释放共享内存,给shmctl函数传参shmid,要查看对应共享内存的属性,给shmctl也要传shmid

2.ftok//产生一个随机值key,保证共享内存的唯一性

#include <sys/types.h>
   #include <sys/ipc.h>
    key_t ftok(const char *pathname, int proj_id);

pathname :路径字符串(任意)

proj_id:任意数值

返回值为key,保证共享内存的唯一性,保证两个进程的参数一样,俩个进程得到相同的key

3.shmat//将对应共享内存映射到当前进程的地址空间中

#include <sys/types.h>
   #include <sys/shm.h>
   void *shmat(int shmid, const void *shmaddr, int shmflg);

shmid是区分共享内存的标识符,shmaddr是要挂在地址空间的地址,如果设置为nullptr,操作系统自己分配,shmflg设置为0即可

返回的是共享内存映射到地址空间的起始地址

4.shmdt//对应的共享内存和进程的地址空间断开映射关系

#include <sys/types.h>
  #include <sys/shm.h>
   int shmdt(const void *shmaddr);

shmaddr为共享内存映射到地址空间的起始地址

5.ipcs -m //查看内存中的共享内存

6.ipcrm -m +shmid号//释放对应的共享内存

7.shmctl//对共享内存管理操作

#include <sys/ipc.h>
  #include <sys/shm.h>
   int shmctl(int shmid, int cmd, struct shmid_ds *buf);

cmd为对共享内存的操作,buf为共享内存的结构体

查看共享内存属性第二个参数传IPC_STAT

释放共享内存第二个参数传IPC_RMID

3.代码实现

1.实现共享内存创建以及两进程拿到一个key和shmid

shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__
#include<iostream>
#include<string>
#include <sys/types.h>//ftok
#include <sys/ipc.h>//ftok
#include <sys/ipc.h>//shmget
#include <sys/shm.h>//shmget
using  namespace std;
const int gCreator=1;
const int gUer=2;
const string pathname="/home/zjw/lesson25";
const int g_id=0x21;
const int gshmsize=4096;
class shm
{
private:
   key_t getcommkey()  //获得key,是用户给系统用来唯一确定一块共享内存
   {
    key_t k=ftok(_pathname.c_str(),_id);//两个都是用户给的
    if(k<0)
    {
        perror("ftok");
    }
    return k;
   }
   int getshmhelper(key_t key,int size,int flag)
   {
   int shmid=shmget(key,size,flag);//创建共享内存的函数//key是用户给操作系统,方便其他进程找到对应的共享内存,size是共享内存的大小//flag是共享内存的权限
    //flag可以区分是创建共享内存的,还是找共享内存的
  if(shmid<0)
  {perror("shmid");}
  return shmid;//shmid<0说明共享内存申请失败,shmid是操作系统给用户的,方便用户对共享内存的操作,比如说删除共享内存
   }
public:
    shm(const string &pathname,int id,int who)
    :_pathname(pathname)
    ,_id(id)
    ,_who(who)
    {
     _key=getcommkey();//相当于初始化时创建端的key和找key的进程一样,因为ftok函数的两个参数都是全局的,返回的key是一样的
     if(_who==gCreator)getshmusecreate();
     else if(_who==gUer)getshmforusr();
        cout<<"shmid:"<<_shmid<<endl;
       cout<<"_key:"<<_key<<endl;
    }
    bool getshmusecreate()
    {
      if(_who==gCreator)
      {
              _shmid= getshmhelper(_key,gshmsize,IPC_CREAT|IPC_EXCL|0666);
              if(_shmid>=0)
                {cout<<"shm create done..."<<endl;
                return true;}
      }
      return false;
    
    }
    bool getshmforusr()
    {
    if(_who==gUer)
      {
       _shmid=getshmhelper(_key,gshmsize,IPC_CREAT|0666);
       if(_shmid>=0)
        { cout<<"shm get done..."<<endl;
         return true;
        }
         
      }
       return false;
    }
~shm()
{
if(_who==gCreator)
  {
   int res=shmctl(_shmid,IPC_RMID,nullptr);//第一个参数是对一个共享内存的标识,给用户,方便用户对共享内存进行操作,IPC_RMID对共享内存的删除
//第三个一个结构体的地址,保存共享内存的属性
  cout<<"shm remove done..."<<endl;
  }
  
}
private:
key_t _key;
int _shmid;
string _pathname;
int _id;
int _who;
}; 
#endif

2.client.cc

#include"shm.hpp"
int main()
{
shm cli(pathname,g_id,gCreator);
while(1)  //防止析构,释放共享内存
{}
}

3.server.cc

#include"shm.hpp"
int main()
{
shm ser(pathname,g_id,gUer);
while(1)
{}
}

共享内存创建,进程拿到key和shmid

2.实现通信

shm.hpp

#ifndef __SHM_HPP__
#define __SHM_HPP__
#include<iostream>
#include<string>
#include <unistd.h>
#include <sys/types.h>//ftok
#include <sys/ipc.h>//ftok
#include <sys/shm.h>//shmget
using  namespace std;
const int gCreator=1;
const int gUer=2;
const string pathname="/home/zjw/lesson25";
const int g_id=0x21;
const int gshmsize=4096;
class shm
{
private:
   key_t getcommkey()  //获得key,是用户给系统用来唯一确定一块共享内存
   {
    key_t k=ftok(_pathname.c_str(),_id);//两个都是用户给的
    if(k<0)
    {
        perror("ftok");
    }
    return k;
   }
   int getshmhelper(key_t key,int size,int flag)
   {
   int shmid=shmget(key,size,flag);//创建共享内存的函数//key是用户给操作系统,方便其他进程找到对应的共享内存,size是共享内存的大小//flag是共享内存的权限
    //flag可以区分是创建共享内存的,还是找共享内存的
  if(shmid<0)
  {perror("shmid");}
  return shmid;//shmid<0说明共享内存申请失败,shmid是操作系统给用户的,方便用户对共享内存的操作,比如说删除共享内存
   }
   void* attachshm()
   {
   
       void*shmaddr=shmat(_shmid,nullptr,0);//对应进程共享内存和地址空间链接,返回的是共享内存在地址空间对应的起始地址
       if(shmaddr==nullptr)
       {
        perror("shmat");
       }
       cout<<"who:>"<<_who<<"< "<<"1是gCreator,2是gUer"<<"对应进程共享内存和地址空间链接"<<endl;
      
       return shmaddr;
   }
   void detachshm(void* shmaddr) 
   {
    if(shmaddr==nullptr)
    return ;
    shmdt(shmaddr);//将共享内存段从调用进程的地址空间中分离。
    //cout<<"who:>"<<_who<<"< "<<"1是gCreator,2是gUer"<<"对应进程共享内存和地址空间断开链接"<<endl;
   }
public:
    shm(const string &pathname,int id,int who)
    :_pathname(pathname)
    ,_id(id)
    ,_who(who)
    {
     _key=getcommkey();//相当于初始化时创建端的key和找key的进程一样,因为ftok函数的两个参数都是全局的,返回的key是一样的
     if(_who==gCreator)getshmusecreate();
     else if(_who==gUer)getshmforusr();
     _addrshm=attachshm();//返回对应共享空间对应在地址空间的起始地址
     
    
    }
    bool getshmusecreate()
    {
      if(_who==gCreator)
      {
              _shmid= getshmhelper(_key,gshmsize,IPC_CREAT|IPC_EXCL|0666);//为了传参给 getshmhelper
              if(_shmid>=0)
                {cout<<"shm create done..."<<endl;
                return true;}
      }
      return false;
    
    }
    bool getshmforusr()
    {
    if(_who==gUer)
      {
       _shmid=getshmhelper(_key,gshmsize,IPC_CREAT|0666);
       if(_shmid>=0)
        { cout<<"shm get done..."<<endl;
         return true;
        }
         
      }
       return false;
    }
~shm()
{ if(_addrshm!=nullptr)
    detachshm(_addrshm);
if(_who==gCreator)
  {
   int res=shmctl(_shmid,IPC_RMID,nullptr);//第一个参数是对一个共享内存的标识,给用户,方便用户对共享内存进行操作,IPC_RMID对共享内存的删除
//第三个一个结构体的地址,保存共享内存的属性
  cout<<"shm remove done..."<<endl;
  }
  
}
void* addr()//让两个进程能拿到类的私有
{
return _addrshm;
}
private:
key_t _key;
int _shmid;
string _pathname;
int _id;
int _who;
void*_addrshm;
}; 
#endif

client.cc

#include"shm.hpp"
#include"namepipe.hpp"
int main()
{
shm cli(pathname,g_id,gCreator);
char*shmaddr=(char*)cli.addr();
sleep(10);
char ch='A';
while(ch<='Z')
{
shmaddr[ch-'A']=ch;
sleep(2);
ch++;
} 
}

server.cc

#include"shm.hpp"
int main()
{
shm ser(pathname,g_id,gUer);
char*shmaddr=(char*)ser.addr();
while(1)
{
cout<<"shm memory content:"<<shmaddr<<endl;
sleep(1);
} 
}

共享内存通信

我们通过对上面的演示,共享内存并不提供对共享内存的保护机制,当没有往共享内存里面写东西的时候,还可以读共享内存

数据不一致问题—共享内存的缺点

我们在访问共享内存时,没有任何的系统调用

共享内存是所有进程IPC,速度最快,共享内存大大减小了数据拷贝次数

解决方法:我们可以通过管道的特性来对共享内存做保护,当我们开始往共享内存写东西了,打开管道的写端,

将管道读端放在读共享内存前面,因为管道写端不写,管道读端就会阻塞,等待管道写,这样就解决了未往共享内存写,就开始读这种缺点

3.解决共享内存的缺点

将之前写的命名管道移到今天lesson25目录下

client.cc

#include"shm.hpp"
#include"namepipe.hpp"
int main()
{
shm cli(pathname,g_id,gCreator);
char*shmaddr=(char*)cli.addr();
Namepipe fifo(com_path,Creater);
fifo.openforwrite();
sleep(10);
char ch='A';
while(ch<='Z')
{
shmaddr[ch-'A']=ch;
string temp="wake";
fifo.writenamepipe(temp);
sleep(2);
ch++;
} 
}

server.cc

#include"shm.hpp"
#include"namepipe.hpp"
int main()
{
shm ser(pathname,g_id,gUer);
Namepipe fifo(com_path,User);
fifo.openforread();
char*shmaddr=(char*)ser.addr();
while(1)
{
string tmp;
fifo.readnamepipe(&tmp);
cout<<"shm memory content:"<<shmaddr<<endl;
sleep(1);
} 
}

namepipe.hpp

#include<iostream>
#include <sys/types.h>  //mkfifo的头文件open头文件
#include<string>
#include <sys/stat.h>   //mkfifo的头文件open头文件
#include <unistd.h>     //unlink头文件/close头文件
#include <fcntl.h>      //open头文件
using namespace std;
const string com_path="./myfifo";
#define Defaultfd -1
#define Creater 1
#define User 2
#define Read O_RDONLY
#define Write O_WRONLY
#define Basesize 1024
class Namepipe
{
   private:
   bool opennamepipe(int mode)
   {
   _fd=open(_fifo_path.c_str(),mode);
   if(_fd<0)
    return false;
    return true;
   }
public: 
Namepipe(const string &path,int who)
:_fifo_path(path)
,_id(who)
,_fd(Defaultfd)
{
 
 if(_id==Creater)
 {
  int ret=mkfifo(_fifo_path.c_str(),0666);
     if(ret!=0)
       {
        perror("mkfifo");
       }
       cout<<"creater create named pipe"<<endl;
 }
}
~Namepipe()
{
if(_id==Creater)
{
 int ret=unlink(_fifo_path.c_str());
 if(ret!=0)
    {
    perror("unlink");
    }
    cout<<"creater free namepipe"<<endl;
}
if(_fd!= Defaultfd)close(_fd);
}
bool openforread()
{
return opennamepipe(Read);
}
bool openforwrite()
{
return opennamepipe(Write);
}
int writenamepipe(const string &in)
{
 return write(_fd,in.c_str(),in.size());
}
int readnamepipe(string *out)
{
char buffer[Basesize];
int n=read(_fd,buffer,sizeof(buffer));
if(n>0)
{buffer[n]=0;
*out=buffer;
}
return n;
}
private:
     const string _fifo_path;
     int _id;
     int _fd;
};

解决共享内存的缺点

目录
相关文章
|
16天前
|
存储 Linux Android开发
Volatility3内存取证工具安装及入门在Linux下的安装教程
Volatility 是一个完全开源的工具,用于从内存 (RAM) 样本中提取数字工件。支持Windows,Linux,MaC,Android等多类型操作系统系统的内存取证。针对竞赛这块(CTF、技能大赛等)基本上都是用在Misc方向的取证题上面,很多没有听说过或者不会用这款工具的同学在打比赛的时候就很难受。以前很多赛项都是使用vol2.6都可以完成,但是由于操作系统更新,部分系统2.6已经不支持了,如:Win10 等镜像,而Volatility3是支持这些新版本操作系统的。
|
1月前
|
算法 安全 Linux
探索Linux内核的虚拟内存管理
【5月更文挑战第20天】 在本文中,我们将深入探讨Linux操作系统的核心组成部分之一——虚拟内存管理。通过剖析其关键组件和运作机制,揭示虚拟内存如何提供高效的内存抽象,支持庞大的地址空间,以及实现内存保护和共享。文章将重点讨论分页机制、虚拟内存区域(VMAs)的管理、页面置换算法,并简要分析这些技术是如何支撑起现代操作系统复杂而多变的内存需求的。
|
10天前
|
监控 Linux 数据处理
探索Linux中的`lsmem`命令:深入了解系统内存布局
`lsmem`是Linux命令,用于显示系统内存布局和大小,帮助管理员和开发者理解内存使用情况。它提供详细输出,包括内存块的大小、范围、类型和关联,支持多种格式展示,如树状图。命令参数如`-h`显示帮助,`-t`以树形展示,`--human-readable`使大小更易读。需root权限运行,常与`free`、`vmstat`等工具结合使用,用于监控和优化内存。注意不同发行版可能存在兼容性差异。
|
9天前
|
存储 缓存 算法
【原创】(十)Linux内存管理 - zoned page frame allocator - 5(1)
【原创】(十)Linux内存管理 - zoned page frame allocator - 5
|
10天前
|
Java Linux PHP
【应急响应】后门攻击检测指南&Rookit&内存马&权限维持&WIN&Linux
【应急响应】后门攻击检测指南&Rookit&内存马&权限维持&WIN&Linux
|
18天前
|
算法 Linux 测试技术
Linux编程:测试-高效内存复制与随机数生成的性能
该文探讨了软件工程中的性能优化,重点关注内存复制和随机数生成。文章通过测试指出,`g_memmove`在内存复制中表现出显著优势,比简单for循环快约32倍。在随机数生成方面,`GRand`库在1000万次循环中的效率超过传统`rand()`。文中提供了测试代码和Makefile,建议在性能关键场景中使用`memcpy`、`g_memmove`以及高效的随机数生成库。
|
2天前
|
监控 Linux
深入了解Linux的pmap命令:进程内存映射的利器
`pmap`是Linux下分析进程内存映射的工具,显示内存区域、权限、大小等信息。通过`/proc/[pid]/maps`获取数据,特点包括详细、实时和灵活。参数如`-x`显示扩展信息,`-d`显示设备。示例:`pmap -x 1234`查看进程1234的映射。注意权限、实时性和准确性。结合其他工具定期监控,排查内存问题。
|
3天前
|
存储 机器学习/深度学习 Linux
程序员必知:关于Linux内存寻址与页表处理的一些细节
程序员必知:关于Linux内存寻址与页表处理的一些细节
|
4天前
|
Linux 芯片
一篇文章讲明白Linux内核态和用户态共享内存方式通信
一篇文章讲明白Linux内核态和用户态共享内存方式通信
|
26天前
|
消息中间件 算法 Unix
【Linux】System V 共享内存
【Linux】System V 共享内存