首页 > 系统相关 >共享内存的理解

共享内存的理解

时间:2024-09-16 13:23:24浏览次数:3  
标签:__ include int 理解 key 共享内存 shm

 

目录

直接原理

原理图​编辑

直接代码

创建唯一键值代码 

创建共享内存代码 

共享内存的指令操作 

 简单封装

补充指令集



系统设计的只能本地进行通信;

直接原理

共享内存也是进程间通信的方案

原理图

理解:

1.   所有图中所说的操作都是os所做的 

2.   os必须提供1和2步骤的系统调用,供进程A和B进行调用

3.   共享内存在os中可以同时存在多份(因为不止A和B进程,可能还有E和F,G和J等),供不同对进程同时进行通信

4.   os注定了要对共享内存进行管理!------先描述后组织--------决定了共享内存不是简单的一段内存空间,也要有描述并管理共享内存的数据结构和匹配的算法!

5.   共享内存=内存空间(数据)+共享内存的操作

直接代码





这个shmget完成的是1步骤,并且是系统调用;
ftok 是一个用于生成唯一的系统V IPC (Inter-Process Communication) 标识符的函数,用于根据文件路径和一个项目标识符生成一个唯一的键值;pathname: 指向一个文件路径的字符串。ftok 将使用这个文件的 inode 信息来生成键值;proj_id: 一个项目标识符,通常是一个字符,用于在同一文件路径下区分不同的键值,通常是单字符的整数,范围从 0 到 255;

创建唯一键值代码 

shm.hpp

//shm是共享内存的缩写
#ifndef __SHM_HPP__ 
//检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。
#define __SHM_HPP__ 
//定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。

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

const std::string pathname="/root/pipe/4.shm";//绝对路径
const int proj_id=0x66;//项目标识符

key_t getcommkey(const std::string & pathname, int proj_id){
    key_t k=ftok(pathname.c_str(),proj_id); //生成键值
    if(k<0){
        perror("ftok");
    }
    return k;
}

#endif 
//结束 #ifndef 的条件编译指令。

client.cc

#include"shm.hpp"
int main(){
    key_t key=getcommkey(pathname,proj_id);
    std::cout<<"key: "<<key<<std::endl;
    return 0;
}

serve.cc 

#include"shm.hpp"
int main(){
    key_t key=getcommkey(pathname,proj_id);
    std::cout<<"key: "<<key<<std::endl;
    return 0;
}

创建共享内存代码 

shm.hpp

//shm是共享内存的缩写
#ifndef __SHM_HPP__ 
//检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。
#define __SHM_HPP__ 
//定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。

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

const std::string pathname="/root/pipe/4.shm";//绝对路径
const int proj_id=0x66;//项目标识符

key_t getcommkey(const std::string & pathname, int proj_id){//创建唯一键值
    key_t k=ftok(pathname.c_str(),proj_id); //生成键值
    if(k<0){//失败
        perror("ftok");
    }
    return k;
}

int shmgget(key_t key,int size){//创建共享内存
    int shmid=shmget(key,size,IPC_CREAT | IPC_EXCL);//返回值是共享内存的标识符
    if(shmid<0){//失败
        perror("shmget");
    }
    return shmid;
}

#endif 
//结束 #ifndef 的条件编译指令。

silent.cc

#include"shm.hpp"
int main(){
    key_t key=getcommkey(pathname,proj_id);
    std::cout<<"key: "<<key<<std::endl;
    int shmid=shmgget(key,4096);
    std::cout<<"shmid: "<<shmid<<std::endl;
    return 0;
}


为什么第二次就失败了呢?

因为文件已存在,因为我创建时用的是IPC_CREAT | IPC_EXCL(不存在就创建,存在就出错返回)第二次调用原本就有了所以第二次出错返回;

而且共享内存不随着进程的释放而释放,所以就第二次报错;他会一直存在直到系统重启,所以需要手动释放(指令或者其他系统调用)。

他的生命周期随内核,文件的生命周期随进程;

共享内存的指令操作 

 ipcs -m 命令用于显示系统中的共享内存段信息。它会列出所有当前存在的共享内存段的详细信息,比如标识符、大小、创建时间等。这个命令有助于你了解系统内存的使用情况。

删除的话只能用shmid删除:   ipcrm -m 是一个用于删除共享内存段的命令



可以理解为key是内核用的,shmid是用户用的;

注意:
在删除共享内存段之前,请确保没有任何进程正在使用它。
删除共享内存段会释放该内存区域,并且该共享内存段中的数据将会丢失。

 删除之后再运行就能跑,细节是shmid变化了,key没变

 简单封装

shm.hpp

// shm是共享内存的缩写
#ifndef __SHM_HPP__
// 检查 __SHM_HPP__ 这个宏是否未定义。如果未定义,则执行接下来的代码,直到遇到 #endif。
#define __SHM_HPP__
// 定义 __SHM_HPP__ 宏。这样,如果这个头文件再次被包含,#ifndef 检查将失败,文件内容不会被再次处理,从而避免了重复定义的问题。

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

const int creater = 1;
const int user = 2;
const std::string gpathname = "/root/pipe/4.shm"; // 绝对路径
const int gproj_id = 0x66;                        // 项目标识符

class shm
{
private: // 不用暴露给用户
    key_t getcommkey()
    {                                                // 创建唯一键值
        key_t k = ftok(_pathname.c_str(), _proj_id); // 生成键值
        if (k < 0)
        { // 失败
            perror("ftok");
        }
        return k;
    }
    int getcomm(key_t key, int size, int flag) // 创建共享内存的共同方法
    {
        int shmid = shmget(key, size, flag); // 返回值是共享内存的标识符
        if (shmid < 0)
        { // 失败
            perror("shmget");
        }
        return shmid;
    }

public:
    shm(const std::string &pathname, int proj_id, int who) // 顺便告诉是谁
        : _pathname(pathname), _proj_id(proj_id), _who(who)
    {
        _key = getcommkey(); // 键值初始化
        if (_who == creater)
            getshmforcreat();
        else if (_who == user)
            getshmforuse();
        std::cout << "key: " << tohex(_key) << std::endl; // 16进制打印
        std::cout << "shmid: " << _shmid << std::endl;
    }
    ~shm()
    {
        if (_who == creater)
        {                                                // 只有创建者才能删除
            int res = shmctl(_shmid, IPC_RMID, nullptr); // 进行删除操作通过调用系统接口
        }
        std::cout << "shm remove done " << std::endl;
    }
    std::string tohex(key_t key)
    { // 转化成16进制
        char buffer[128];
        snprintf(buffer, sizeof(buffer), "0x%x", key);
        return buffer;
    }
    bool getshmforcreat()
    { // 创建者
        if (_who == creater)
        {
            _shmid = getcomm(_key, 4096, IPC_CREAT | IPC_EXCL); // 调用
            if (_shmid >= 0)
            {
                std::cout << "shm create done " << std::endl;
                return true; // 成功
            }
        }
        return false;
    }
    bool getshmforuse()
    { // 使用者
        if (_who == user)
        {
            _shmid = getcomm(_key, 4096, IPC_CREAT); // user的话只用IPC_CREAT即可
            if (_shmid >= 0)
            {
                std::cout << "shm get done " << std::endl;
                return true; // 成功
            }
        }
        return false;
    }

private:
    key_t _key;
    int _shmid;

    int _proj_id;
    std::string _pathname;

    int _who;
};

#endif
// 结束 #ifndef 的条件编译指令。

serve.cc

#include"shm.hpp"

int main(){
    shm sshm(gpathname,gproj_id,creater);
    return 0;
}

client.cc 

#include"shm.hpp"

int main(){
    shm sshm(gpathname,gproj_id,user);
    return 0;
}



我先执行serve后执行client,导致执行client时又创建了shm,因为user是IPC_CREAT,而且打印的"shm remove done "没有删除因为他不是creater;

而serve运行后会创建又删除;

如果太快的话可以用sleep()在创建的语句停顿一下;

 shmctl 是一个用于控制共享内存段的系统调用。

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:
shmid:共享内存段的标识符。
cmd:操作命令,决定要执行的具体操作。
buf:指向 shmid_ds 结构的指针,用于存储共享内存段的信息或设置参数。
常用命令 (cmd):
IPC_STAT:获取共享内存段的状态。
IPC_SET:设置共享内存段的属性。
IPC_RMID:删除共享内存段。

补充对共享内存理论的理解

共享内存的接口

补充指令集

IPC的指令

标签:__,include,int,理解,key,共享内存,shm
From: https://blog.csdn.net/yiqizhuashuimub/article/details/142286249

相关文章

  • 深入理解Nova组件与虚拟机生命周期管理
    在OpenStack学习旅程的第32天,我们深入探索了Nova这一核心组件,它是OpenStack云中负责计算资源管理和虚拟机生命周期控制的“大脑”。Nova不仅负责管理虚拟机的创建、调度、执行和销毁,还提供了丰富的API接口,使得用户能够灵活地与云基础设施进行交互。虚拟机生命周期管理虚拟机的生命......
  • 并发与并行的区别:深入理解Go语言中的核心概念
    在编程中,并发与并行的区别往往被忽视或误解。很多开发者在谈论这两个概念时,常常把它们混为一谈,认为它们都指“多个任务同时运行”。但实际上,这种说法并不完全正确。如果我们深入探讨并发和并行的区别,会发现它不仅是词语上的不同,更是编程中非常重要的抽象层次,特别是在Go语言......
  • 深入理解Servlet
    引言  Servlet是一门很古老的技术了,网上很多人说没必要学了,可以直接学习Springboot+vue进行开发。但Servlet作为‌JavaEE规范的一部分,是这些框架的基础。掌握Servlet对于理解现代Web开发框架至关重要。‌当然每个人有自己独特的见解,写这篇文章方便自己回顾,也希望能帮助......
  • Android HandlerThread Post后延迟7秒才执行的原因及解决方案|如何提高Android后台线程
    在Android开发中,HandlerThread是用于处理后台线程任务的常见工具。然而,有时我们会遇到这样的问题:当任务通过HandlerThread的post方法发送后,任务的执行时间会出现明显的延迟,比如7秒的延迟才执行任务。本文将深入分析这种问题的成因,探讨可能的影响因素,并提供多种优化方案,帮助开发者解......
  • 深入理解redis删除策略和淘汰策略
    1、redis的删除策略Redis是一种内存级数据库,数据都存在内存中,但是针对于已经过期的数据,reids不会立刻删除只是会存储在 expires 中,当执行删除策略的时候,才会从expires中寻找对应的数据存储的地址,在存储空间中找到对应的数据进行删除。数据删除其实就是内存和CPU占用之间寻......
  • 深入理解Redis锁与Backoff重试机制在Go中的实现
    目录Redis锁的深入实现Backoff重试策略的深入探讨结合Redis锁与Backoff策略的高级应用具体实现结论在构建分布式系统时,确保数据的一致性和操作的原子性是至关重要的。Redis锁作为一种高效且广泛使用的分布式锁机制,能够帮助我们在多进程或分布式环境中同步访问共享资源。本文将深......
  • AutoSar AP平台的SOMEIP文档的理解笔记
    前言前段时间,阅读了AutoSarAP的SOME/IP的标准文档(《SOME/IPProtocolSpecification.pdf》),并以PPT的图文并茂的形式做了理解笔记,内容主要是SOME/IP的协议规范,由SOME/IP报文格式和协议部分。1.SOMEIP报文格式1.1SOME/IP消息格式:头格式1.2SOME/IP头格式:RequestID(Clie......
  • Git缓冲区理解:`index`,`add`和`reset`,`staged`和`unstaged`
    在git里面,有一个叫index的区域,你把东西加到那里叫add,把东西再从哪里撤回来叫reset;已经在里面的我们形容它是staged,还没有加进去的我们形容它是unstaged。其实index区就是一个纯粹的缓冲区,也叫stagingarea,是正式提交之前给我们的一个缓冲,还有犹豫的余地。因为一旦正式commit提交......
  • 深入理解指针(4)
    1.回调函数是什么?1.回调函数是什么?回调函数就是⼀个通过函数指针调⽤的函数。如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件......
  • 深入理解 Write-Ahead Logging (WAL) 及其应用
    在讨论数据库原理的时候,我们经常会听到一种技术-Write-AheadLogging(WAL),它保证了数据的持久性和一致性。WAL的基本思想非常简单,但它的应用范围非常广泛,从数据库到分布式系统,再到各种现代应用的开发中,都能看到它的影子。本文将深入剖析WAL的基本机制,并探讨其在不同应用场......