草庐IT

【Linux】system V 共享内存

风起、风落 2023-08-18 原文

文章目录

system V

system V 是一套标准,独立于文件系统之外的,专门为了通信设计出来的模块
让两个毫不相关的进程看到同一份资源

1. 共享内存原理

第一阶段原理

进程A和进程B都通过自己的页表映射到物理内存中的特定区域,进而找到该进程匹配的代码和数据
为了让进程A和进程B通信,前提是两者要看到同一份资源
假设在物理内存上开辟一块空间
进程A和进程B在自己的地址空间中都有自己的共享区
想办法把物理内存中新开辟空间 通过页表 映射到 进程A和进程B的共享区中
把地址空间的起始地址返回给用户
进程A和进程B就可以通过起始的虚拟地址,对应页表访问到内存
就完成了让进程A和进程B看到同一份资源,这份资源就被称为共享内存

第二阶段原理

系统中可以用ssh进行通信 ,是不是只能有一对进程使用共享内存呢?
可以,其他进程也可以通信
所以在任何时刻,可能有多个共享内存在被使用
系统中一定会存在很多共享内存同时存在
操作系统要不要整体管理所有的共享内存呢?要
操作性系统如何管理多个共享内存呢?
先描述,在组织
并不是在内存中开辟空间即可,系统为了管理共享内存,构建对应的描述共享内存的结构体对象
共享内存=共享内存的内核数据结构(伪代码:struct shm)+真正开辟的内存空间

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


打开vscode,创建文件client.cc和server.cc(后缀为cc说明是c++)的文件
创建公共路径 comm.hpp

shmget函数

创建共享路径接口 ,输入 man shmget 查看

申请一个 系统V的共享内存块
如果创建成功,则会返回共享内存标识符,失败返回-1


size代表申请内存块的大小
shmflg代表 选项
有两个最常用的选项,IPC_CREAT IPC_EXCL
转到定义就可以发现其实这两个都是宏


若单独使用 IPC_CREAT :创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,就获取已经存在的共享内存并返回
IPC_EXCL不能单独使用 ,一般都要配合 IPC_CREAT
若要将两个选项同时传进去 IPC_CREAT | IPC_EXCL
两个选项同时用: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,则立马出错返回
如果创建成功,对应的共享内存一定是最新的


获取共享内存时,需要有一个key值

ftok函数

输入 man ftok

根据路径和项目id进行算法结合,形成一个冲突概率低的key值
失败就返回-1,成功返回key值

key值用法

假设进程A创建了一个共享内存,但是进程B怎么知道那个共享内存是创建的吗?
就需要借助上述提到的 ftok 函数


刚开始约定好 A和B用同样的路径字符串和项目id
借助A形成一个key值,将key值放入A创建的共享内存描述结构体中
此时B也形成一个相同的key值,通过寻找key值来找到A所创建的共享内存


pathname 代表 用户自己设定的路径字符串
proj_id 代表 项目id
key值意义为
让创建共享内存的进程可以给新共享内存设置key值
让获取共享内存的进程 通过key值 去找特定匹配的共享内存

1. 创建key值

comm.hpp 公共路径中构建一个函数 Getkey 用于返回key值


构建一个函数 tohex,用于将数转换为十六进制


通过server.cc与client.cc中分别调用Getkey 与tohex函数


两者的返回值key 是相同的,并且返回的都是十六进制数

2. 创建共享内存 获取共享内存


创建共享内存,调用shmget函数,通过两个选项 若共享内存不存在则创建,若存在则报错
而获取共享内存,调用shmget函数,则返回已有的共享内存


此时运行可执行程序,发现server与client的shmid(共享内存标识符)相同

3. 将自己和共享内存关联起来

输入 man shmat 指令

at代表 关联
将共享内存和目标值关联起来
返回值为 共享内存的虚拟地址的起始地址
我们不知道应该把共享内存放在虚拟空间的什么地址处,所以shmaddr设为NULL让系统自主去选择
shmflg 可以设置为 SHM_RDONLY 表示当前共享内存是只读的 一般设为0,默认为读写的


4. 将自己和共享内存取消关联

输入 man shmdt 指令

shmdt代表 虚拟地址
成功返回0,失败返回-1

5. 删除共享内存

创建共享内存的进程已经早就退出了,但是共享内存还存在
确认共享内存存在: ipcs
ipc作为进程间通信的简写
ipc表示资源 s表示有多个资源


显出来的为ipc通信系统所支持的三种ipc通信策略
Message Queues 消息队列
Shared Memory Segments 共享内存段
Semaphore Arrays 信号量


ipcs - m 查看共享内存段


perms 代表权限
bytes 代表字节数
nattch 代表 有几个进程和当前进程是关联的

用指令删除

key是在操作系统中使用的,类似于文件的inode编号
shmid 类似于文件的fd
所以删除操作,是在用户层,应该用shmid


ipcrm -m shmid值 就可以删除共享内存
此时就没有 shmid为0的共享内存 存在了

调用系统调用

输入 man shmctl 指令


shmid 代表 共享内存描述符 即想对那个共享内存操作
cmd 代表 选项 即想做什么操作
IPC_STAT 获取当前共享内存的属性
IPC_SET 设置共享内存属性
IPC_RMID 标记这个段被释放
buf 代表 共享内存的属性

在comm.hpp下 设置删除共享内存的函数,在server.cc中调用函数 即可删除共享内存

完整代码

makefile

.PHONY:all
all:server client

server:server.cc
	g++ -o $@ $^
client:client.cc
	g++ -o $@ $^
.PHONY:clean
clean:
	rm -f server client 

如何使两个可执行程序运行,在上一篇文章提到过,点击查看:命名管道


comm.hpp

#ifndef _COMM_HPP_
#define _COMM_HPP_
#include<iostream>
#include<cerrno>
#include<cstdio>
#include<cstring>
#include<stdlib.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<sys/types.h>
#include<assert.h>
using namespace std;

#define PATHNAME "."//.表示当前路径  路径字符串
#define PROJID 0x6666 //项目id
const int gsize=4096;
key_t getkey()//用于返回key值
{
    key_t k=ftok(PATHNAME,PROJID);
    if(k==-1)//失败
    {
       cout<<errno<< " :" <<strerror(errno)<<endl;
       exit(1);
    }
    return k;//返回key值
}
string tohex(int x)//转换为十六进制
{
   char buffer[64];
   //将x以十六进制的形式传给buffer
   snprintf(buffer,sizeof(buffer),"0x%x",x);
   return buffer;
}

static int  createshmhelper(key_t k,int size,int flag)//static修饰只在本文件有效
{
     
  int shmid=shmget(k,size,flag);
  if(shmid==-1)//创建失败
  {
    cout<<errno<< " :" <<strerror(errno)<<endl;
       exit(2);
  }
  return  shmid;//返回共享内存标识符
}

int createshm(key_t k,int size)//创建共享内存
{
    //带有两个选项 若不存在则创建,若存在则报错
    return createshmhelper(k,size,IPC_CREAT |IPC_EXCL);
}

int getshm(key_t k,int size)
{
    //若有共享内存,则返回已有的共享内存
     return createshmhelper(k,size,IPC_CREAT );
}

char* attachshm(int shmid)//关联
{
    char*start=(char*)shmat(shmid,NULL,0);//对应类型void* 所以需要强转
     return start;
}

void detachshm(char*start)//取消关联
{
 int n=shmdt(start);
 assert(n!=-1);
 (void)n;
}

void delshm(int shmid)
{
   int n=shmctl(shmid,IPC_RMID,NULL);
   assert(n!=-1);//为-1就删除失败
   (void)n;
}


#endif 

server.cc

#include"comm.hpp"
#include<unistd.h>
int main()
{
    //1. 创建key值
  key_t k=getkey();//获取key值
  cout<<"server:"<<tohex(k)<<endl;

   //2.创建共享内存
   int shmid=createshm(k,gsize);//返回的是共享内存标识符
   cout<<"server shmid:"<<shmid<<endl;//将共享内存标识符转换为十六进制
   sleep(5);
  

   // 3.将自己和共享内存关联起来
   char*start=attachshm(shmid);

   //通信
    
   //4. 将自己和共享内存取消关联
    detachshm(start); 
  

   //5.删除共享内存
    delshm(shmid);
    return 0;
}


client.cc

#include"comm.hpp"

int main()
{
  key_t k=getkey();//获取key值
  cout<<"client key:"<<tohex(k)<<endl;

int shmid=getshm(k,gsize);//获取共享内存
cout<<"client shmid:"<<shmid<<endl;//将共享内存标识符转换为十六进制

  char*start=attachshm(shmid);
   detachshm(start); 
  

  
    return 0;
}

有关【Linux】system V 共享内存的更多相关文章

  1. ruby-on-rails - Ruby net/ldap 模块中的内存泄漏 - 2

    作为我的Rails应用程序的一部分,我编写了一个小导入程序,它从我们的LDAP系统中吸取数据并将其塞入一个用户表中。不幸的是,与LDAP相关的代码在遍历我们的32K用户时泄漏了大量内存,我一直无法弄清楚如何解决这个问题。这个问题似乎在某种程度上与LDAP库有关,因为当我删除对LDAP内容的调用时,内存使用情况会很好地稳定下来。此外,不断增加的对象是Net::BER::BerIdentifiedString和Net::BER::BerIdentifiedArray,它们都是LDAP库的一部分。当我运行导入时,内存使用量最终达到超过1GB的峰值。如果问题存在,我需要找到一些方法来更正我的代

  2. ruby - 通过 ruby​​ 进程共享变量 - 2

    我正在编写一个gem,我必须在其中fork两个启动两个webrick服务器的进程。我想通过基类的类方法启动这个服务器,因为应该只有这两个服务器在运行,而不是多个。在运行时,我想调用这两个服务器上的一些方法来更改变量。我的问题是,我无法通过基类的类方法访问fork的实例变量。此外,我不能在我的基类中使用线程,因为在幕后我正在使用另一个不是线程安全的库。所以我必须将每个服务器派生到它自己的进程。我用类变量试过了,比如@@server。但是当我试图通过基类访问这个变量时,它是nil。我读到在Ruby中不可能在分支之间共享类变量,对吗?那么,还有其他解决办法吗?我考虑过使用单例,但我不确定这是

  3. ruby-on-rails - Ruby 中的内存模型 - 2

    ruby如何管理内存。例如:如果我们在执行过程中采用C程序,则以下是内存模型。类似于这个ruby如何处理内存。C:__________________|||stack|||------------------||||------------------|||||Heap|||||__________________|||data|__________________|text|__________________Ruby:? 最佳答案 Ruby中没有“内存”这样的东西。Class#allocate分配一个对象并返回该对象。这就是程序

  4. 键删除后 ruby​​ 哈希内存泄漏 - 2

    你好,我无法成功如何在散列中删除key后释放内存。当我从哈希中删除键时,内存不会释放,也不会在手动调用GC.start后释放。当从Hash中删除键并且这些对象在某处泄漏时,这是预期的行为还是GC不释放内存?如何在Ruby中删除Hash中的键并在内存中取消分配它?例子:irb(main):001:0>`ps-orss=-p#{Process.pid}`.to_i=>4748irb(main):002:0>a={}=>{}irb(main):003:0>1000000.times{|i|a[i]="test#{i}"}=>1000000irb(main):004:0>`ps-orss=-p

  5. ruby - 在模块/类之间共享全局记录器 - 2

    在许多ruby​​类之间共享记录器实例的最佳(正确)方法是什么?现在我只是将记录器创建为全局$logger=Logger.new变量,但我觉得有更好的方法可以在不使用全局变量的情况下执行此操作。如果我有以下内容:moduleFooclassAclassBclassC...classZend在所有类之间共享记录器实例的最佳方式是什么?我是以某种方式在Foo模块中声明/创建记录器还是只是使用全局$logger没问题? 最佳答案 在模块中添加常量:moduleFooLogger=Logger.newclassAclassBclassC..

  6. ruby-on-rails - HTTParty 的内存问题和下载大文件 - 2

    这会导致Ruby出现内存问题吗?我知道如果大小超过10KB,Open-URI会写入TempFile。但是HTTParty会在写入TempFile之前尝试将整个PDF保存到内存吗?src=Tempfile.new("file.pdf")src.binmodesrc.writeHTTParty.get("large_file.pdf").parsed_response 最佳答案 您可以使用Net::HTTP。参见thedocumentation(特别是标题为“流媒体响应机构”的部分)。这是文档中的示例:uri=URI('http://e

  7. ruby - 如何使用 cucumber 在场景之间共享状态 - 2

    我有一个功能“从外部网站导入文章”。在我的第一个场景中,我测试从外部网站导入链接列表。Feature:ImportingarticlesfromexternalwebsiteScenario:Searchingarticlesonexample.comandreturnthelinksGiventhereisanImporterAnditsURLis"http://example.com"Whenwesearchfor"demo"ThentheImportershouldreturn25linksAndoneofthelinksshouldbe"http://example.com/d

  8. 【Linux操作系统】——网络配置与SSH远程 - 2

    Linux操作系统——网络配置与SSH远程安装完VMware与系统后,需要进行网络配置。第一个目标为进行SSH连接,可以从本机到VMware进行文件传送,首先需要进行网络配置。1.下载远程软件首先需要先下载安装一款远程软件:FinalShell或者xhell7FinalShellxhell7FinalShell下载:Windows下载http://www.hostbuf.com/downloads/finalshell_install.exemacOS下载http://www.hostbuf.com/downloads/finalshell_install.pkg2.配置CentOS网络安装好

  9. Linux磁盘分区中物理卷(PV)、卷组(VG)、逻辑卷(LV)创建和(LVM)管理 - 2

    文章目录一基础定义二创建逻辑卷2-1准备物理设备2-2创建物理卷2-3创建卷组2-4创建逻辑卷2-5创建文件系统并挂载文件三扩展卷组和缩减卷组3-1准备物理设备3-2创建物理卷3-3扩展卷组3-4查看卷组的详细信息以验证3-5缩减卷组四扩展逻辑卷4-1检查卷组是否有可用的空间4-2扩展逻辑卷4-3扩展文件系统五删除逻辑卷5-1备份数据5-2卸载文件系统5-3删除逻辑卷5-4删除卷组5-5删除物理卷六LVM逻辑卷缩容6-1缩容注意事项6-2标准缩容步骤一基础定义LVM,LogicalVolumeManger,逻辑卷管理,Linux磁盘分区管理的一种机制,建立在硬盘和分区上的一个逻辑层,提高磁盘分

  10. ruby - Sinatra 路由中定义的全局变量是否在请求之间共享? - 2

    假设我有:get'/'do$random=Random.rand()response.body=$randomend如果我每秒有数千个请求到达/,$random是否会被共享并“泄漏”到上下文之外,或者它会像getblock的“本地”变量一样?我想如果它是在get'/'do的上下文之外定义的,它确实会被共享,但我想知道在ruby​​中是否有我不知道的$机制。 最佳答案 ThispartoftheSinatraREADMEaboutscopeisalwayshelpfultoread但是,如果您只需要为请求保留变量,那么我认为我建议使用

随机推荐