当前位置: 首页 > news >正文

system V 共享内存

system V是一种标准,linux内核支持这种标准,专门设计了一个ipc模板(通信的接口设计,原理,接口,相似性)

使用各自的虚拟地址访问物理内存

共享内存=描述共享内存的内核数据结构+它所对应的物理空间


进程间通信的本质:让不同的进程先看到同一份资源

进程都有自己的地址空间,都要把地址空间映射到物理内存处

先在物理内存上申请一部分内存空间,通过页表映射到进程的共享区(地址空间)


通过映射让两个不同的进程看到同一个内存

释放共享内存

存在有多组进程,都在使用不同的共享内存来进行通信。OS内,多个共享内存同时存在,就要管理共享内存,先描述再组织


描述共享内存的结构体

共享内存是否被使用?描述共享内存的结构体有引用计数可以来判断


共享内存接口的使用

先创建共享内存

传入key区分唯一性

让用户层约定一个key,一个创建设置到内存当中一个拿着key去查找就能拿到

ftok构建一个key值,把这个key值传递给shmget,创建共享内存的那一方就能构建出带key的共享内存,另一方通过相同的ftok构建相同的key就能使用同一个共享内存

用户指定key


返回值

成功构建共享内存的返回值

标识我们创建好的共享内存

shmget函数

不同的进程shm来解析通信,标识共享内存的唯一性,key来区分,不是内核直接形成的,而是在用户层,构建并传入给OS的

我们怎么评估共享内存是否存在,怎么保证两个不同的进程拿到的是同一个共享内存-->key

shmflg:由九个权限标志组成,他们的用法和创建文件时使用的莫得模式标志是一样的

        IPC_CREAT:创建共享内存,如果目标共享内存不存在就创建;否则就打开这个已经存在的共享内存并返回      

        IPC_CREAT | IPC_EXCL: IPC_EXCL单独存在无意义,如果要创建的shm不存在就创建它,如果已经存在,shmget就会出错返回,只要shmget成功返回,一定是一个全新的共享内存


ftok

代码

进程结束,如果没有进行共享内存的删除,共享内存资源会一直存在 ,即共享内存的资源,生命周期随内核。如果没有显式地删除,即便进程退出了,IPC资源依旧被占用

删除,控制共享内存,在用户层我们不能使用key,key未来只给内核来进行唯一性的区分,需要使用shmid进行共享内存的管理

shmctl函数

我们也可以写入删除IPC对象的代码

用于控制内存

shmid:由shmget返回的共享内存标识码

cmd:将要采取的动作

buf:指向一个保存共享内存的模式状态和访问权限的数据结构

成功返回0,失败返回-1

加上这一段

并修改server.cc

shmat函数

将共享内存段链接到进程地址空间中,shmat:at:attach 关联

shmid:共享内存标识符

shmadder:指定连接的地址(虚拟地址,固定地址进行挂接)

shmflg:它的两个取值是SHM_RNDSHM_RDONLY

成功返回一个指针,指向共享内存的第一个节

失败返回-1

挂接

Attach 函数
功能:该函数用于将创建好的共享内存段附着(映射)到当前进程的地址空间,这样进程就可以通过返回的虚拟地址来访问共享内存中的数据

VirturalAddr 函数
功能:用于获取并打印共享内存的虚拟地址,同时返回该虚拟地址指针,方便其他地方使用该指针来访问共享内存

comm.hpp

#pragma once

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

const int gdefaultid = -1;
//gdefaultid:初始化为 -1,用作共享内存标识符(shmid)的默认值。在后续代码里,
//若 shmid 为 -1,就表明共享内存尚未成功创建或获取
const int gsize = 4096;
//设定共享内存段的大小为 4096 字节
const std::string pathname = ".";
//指定为当前目录 ".",这是 ftok 函数所需的路径名,ftok 会依据此路径名和项目 ID 生成一个键值(key)
const int projid = 0x65;
//ftok 函数所需的项目 ID
const int gmode = 0666;

#define ERR_EXIT(m) \
    do              \
    {               \
        perror(m);\   
        exit(EXIT_FAILURE);\
    }while(0)
//ERR_EXIT 是一个宏,其用途是处理错误情况。
//do {... } while(0) 这种结构保证了宏在被使用时,无论是否处于 if、for 等语句块里,都能正常运行。
//perror(m) 会把错误信息输出到标准错误流,同时显示 m 这个自定义的错误提示。
//exit(EXIT_FAILURE) 会让程序终止并返回一个表示失败的状态码

class Shm
{
private:
 //创建的一定要是个全新的共享内存
 void CreateHelper(int flg)
 {
     key_t k = ftok(pathname.c_str(), projid);
     // pathname 和 projid 生成一个键值 k
     if(k < 0)
     {
         ERR_EXIT("ftok");
        // ftok 函数调用失败,会返回一个小于 0 的值,此时调用 ERR_EXIT("ftok")
        //输出错误信息并终止程序
     }
     printf("key: 0x%x  ", k);//ftok 调用成功,就打印生成的键值 k
     //_shmid = shmget(k, _size, IPC_CREAT | IPC_EXCL | 0666);//也要有权限设置
     _shmid = shmget(k, _size, flg);
     //调用 shmget 函数来创建共享内存段。IPC_CREAT 表示若共享内存段不存在就创建它,
     //IPC_EXCL 表示若共享内存段已经存在则返回错误
     if(_shmid < 0)
     {
         ERR_EXIT("shmget");
         // shmget 函数调用失败,会返回一个小于 0 的值,此时调用 ERR_EXIT("shmget") 
         //输出错误信息并终止程序
     }
     printf("shmid:%d\n  ", _shmid);
     //调用成功,就打印创建的共享内存段的标识符 _shmid
 }
public:
    Shm():_shmid(gdefaultid), _size(gsize), _start_mem(nullptr)
    //_shmid 用于存储共享内存标识符,_size 用于存储共享内存的大小
    {

    }
    void Creat()
    {
        CreateHelper( IPC_CREAT | IPC_EXCL | gmode);
    }
   

    //获取
    void Get()
    {
        CreateHelper( IPC_CREAT );
    }


    void Destroy()
    {
        if(_shmid == gdefaultid) return;
        // 检查共享内存标识符是否为默认值(即未创建共享内存)
        int n = shmctl(_shmid, IPC_RMID, nullptr);
        // 调用 shmctl 函数,尝试删除共享内存段
        if(n > 0)
        {
            printf("shmctl delete shm: %d sucess!\n", _shmid);
            //表示删除成功,输出成功信息
        }
        else
        {
            ERR_EXIT("shmctl");
            //删除失败,调用 ERR_EXIT 宏处理错误
        }
    }
    void Attach()
    {
        _start_mem = shmat(_shmid,nullptr,0);
        if((long long)_start_mem < 0)
        //在进行类型转换为 long long 后就是小于 0 的值
        {
            ERR_EXIT("shmat");
        }
        printf("attach succcess\n");
    }
    void *VirturalAddr()
    {
        printf("*VirturalAddr: %p\n", _start_mem);
        //使用 printf 函数,以指针格式(%p)打印出共享内存的虚拟地址
        return _start_mem;
        //返回指向共享内存起始地址的指针 _start_mem,
        //供其他函数使用该指针来操作共享内存中的数据
    }
private:
    int _shmid;
    int _size;
    void *_start_mem;
};

IPC本质:让不同的进程先看到同一份资源

读写共享内存并没有出现系统调用,共享内存属于用户空间,可以让用户直接使用

共享内存是进程间通信中速度最快的方式:

1.映射之后,读写直接被对方看到

2.不需要进行系统调用获取或者写入内容

但是也会有缺点:通信双方没有所谓的“同步机制”,导致数据不一致

共享内存没有保护机制,即对共享内存中数据的保护

我们shmget(key)就会被设置到共享内存的描述结构体中

int shmctl(int shmid, int cmd, struct shmid_ds *buf);




struct shmid_ds {
               struct ipc_perm shm_perm;    /* Ownership and permissions */
               size_t          shm_segsz;   /* Size of segment (bytes) */
               time_t          shm_atime;   /* Last attach time */
               time_t          shm_dtime;   /* Last detach time */
               time_t          shm_ctime;   /* Creation time/time of last
                                               modification via shmctl() */
               pid_t           shm_cpid;    /* PID of creator */
               pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */
               shmatt_t        shm_nattch;  /* No. of current attaches */
               ...
           };


struct ipc_perm {
               key_t          __key;    /* Key supplied to shmget(2) */
               uid_t          uid;      /* Effective UID of owner */
               gid_t          gid;      /* Effective GID of owner */
               uid_t          cuid;     /* Effective UID of creator */
               gid_t          cgid;     /* Effective GID of creator */
               unsigned short mode;     /* Permissions + SHM_DEST and
                                           SHM_LOCKED flags */
               unsigned short __seq;    /* Sequence number */
           };

在内核中,共享内存在创建的时候,它的大小必须是4KB(4096)的整数倍

如果为4090 --> 4096*2 --> 向上4KB取整

但是能用的也只是4090的内存大小

相关文章:

  • React 记账本项目实战:多页面路由、Context 全局
  • Dolphinscheduler3.2.1运行Java Jar路径重复的BUG修复问题
  • MySQL 用 limit 影响性能的优化方案
  • 深入学习OpenCV:第一章简介
  • (二十二)安卓开发中的数据存储之SQLite简单使用
  • 《轨道力学导论》——第一讲:轨道力学概述
  • 案例驱动的 IT 团队管理:创新与突破之路: 第四章 危机应对:从风险预见到创新破局-4.1.2债务评估模型与优先级排序
  • 阻塞与非阻塞等待非阻塞轮询
  • 代码,Java Maven项目打包遇到的环境问题
  • 针对OPPO A5(PBAM00)在锁屏界面屏幕无法滑动的问题.
  • STM32 HAL库 HC-05蓝牙通信实现
  • 《嵌入式系统原理》一些题目
  • Mysql5.7配置文件
  • StickyNotes,简单便签超实用
  • 变点分组法是一种时序数据处理与分段分析的方法
  • 聊聊Spring AI的Prompt
  • 20250414| AI:RAG多路召回和融合重排序技术
  • Android Studio 在 Windows 上的完整安装与使用指南
  • TreeMap和HashMap的区别
  • CST1020.基于Spring Boot+Vue汽车租赁管理系统
  • 专访|韩国世宗研究所中国研究中心主任:李在明若上台将推行均衡外交
  • 世界期待中美对话合作带来更多确定性和稳定性
  • 中国科学院院士徐春明不再担任山东石油化工学院校长
  • 干部任职公示:陕西宁强、镇安两县县长拟进一步使用
  • 默茨首访聚焦欧洲,欲推欧洲防务自主
  • 著名军旅作家、文艺评论家周政保逝世,享年77岁