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

[Linux打怪升级之路]-system V共享内存

前言

作者小蜗牛向前冲

名言我可以接受失败,但我不能接受放弃

  如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正

本期学习目标:认识什么是 system V共享内存,认识共享内存的接口函数,学会运用共享内存

目录

一、共享内存的基本原理

1、什么是共享内存

2、共享内存和管道的对比

二、共享内存的系统接口 

1、shmget函数(创建共享内存)

 2、shmat函数(关联)

3、shmdt函数(去关联) 

4、shmctl函数(控制)

​三、用共享内存进行通信

1、comm.hpp

2、server.cpp 

 3、client.cpp

4、现象


一、共享内存的基本原理

1、什么是共享内存

共享内存是一种进程间通信机制,它允许两个或多个进程共享同一块物理内存空间,从而实现数据共享。在共享内存中,进程可以通过读写共享内存的方式来相互通信,而不必进行复杂的管道、消息队列等进程间通信操作。

那我们知道了共享内存,其实就是OS操作系统管理的一块共享物理内存,那共享内存是怎么样实现进程间通信的呢?

下面我们看图来理解

首先我们让操作系统在物理内存中,创建一块共享内存。然后在将创建的内存通过页表映射到进程地址空间,这样 A和B进程就通过共享内存建立起来联系,A进程如果想和B进程通信,只要让A进程往物理内存中写数据,在让B进程读数据就可以了。最后我们不在想让AB进程进行通信,我们只要去关联就可以了,AB进程的关联无非是共享的物理内存,所以我们只要取消二者的映射关系,在释放内存即可。

那这种方式和我们前面将的管道将有什么优缺点吗?

客官别走,且听我细细道来。

2、共享内存和管道的对比

共享内存和管道是两种不同的进程间通信机制,它们各有优势和适用场景。下面是它们的对比:

  1. 数据传输方式:

    • 共享内存:进程直接访问同一块物理内存,数据在内存中共享。
    • 管道:通过缓冲区进行数据传输,数据在缓冲区中依次流动。
  2. 通信效率:

    • 共享内存:由于直接访问内存,读写效率高,适用于大量数据交换的场景。
    • 管道:数据需要经过内核空间,相对较慢,适用于小量数据传输或者不频繁的通信。
  3. 数据同步和通信方式:

    • 共享内存:需要考虑并发控制和同步问题,通信方式更为灵活,可以使用类似锁、信号量等机制进行同步。
    • 管道:基于先进先出的原则,数据流动是单向的,不需要显式的同步操作。
  4. 可扩展性:

    • 共享内存:多个进程可以同时访问共享内存,适用于多个生产者和消费者的情况。
    • 管道:一般情况下,只支持一个生产者和一个消费者,不适用于多个进程之间的数据交换。

综上所述,共享内存适用于大量数据交换、需要高效率和灵活同步的场景,而管道适用于小量数据传输或者不频繁的通信,并且只涉及一个生产者和一个消费者

二、共享内存的系统接口 

1、shmget函数(创建共享内存)

功能:用来创建共享内存

原型: int shmget(key_t key, size_t size, int shmflg);

参数

           key:这个共享内存段名字

           size:共享内存大小

           shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样

返回值成功返回一个非负整数,即该共享内存段的标识码(shmid);失败返回-1

key是什么 

 key是共享内存段的名字,我们要注意他是多少我们不关系,我们关心的是他的作用是能够进行唯一的标识。

虽然我们不关系他是多少,但是我们要传什么key给shmegt,key_t 是一个什么类型。

这里我们先看一个获取k的函数ftok()

。那key_t是个什么类型?

key_t是一个在Unix/Linux系统中用于表示IPC(进程间通信)键的类型。它被定义为一个整数类型(通常是int,用于唯一标识共享内存、消息队列和信号量等进程间通信机制。

 举例子获取k:

#define PATHNAME "."
#define PROJ_ID 0x66key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);if(k < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(1);}return k;
}

其中的ftok() 函数使用由给定路径名命名的文档的标识(必须引用现有的、可访问的文档)和最不重要的 8 位proj_id(必须为非零)生成一个key_t类型的 System V IPC 密钥

   shmflg是什么

   shmflg是标志位,用于指定操作共享内存的方式这里我们举例二种最常见的标志位:

IPC_CREAT:如果共享内存不存在就创建,如果存在就获取他。

IPC_EXCL:无法单独使用,IPC_CREAT|IPC_EXCL如果不存在创建共享内存,如果存在就出错返回。

shmget的用法:

#define MAX_SIZE 4096
int getShmHelper(key_t k, int flags)
{int shmid = shmget(k,MAX_SIZE,flags);if(shmid < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(2);}return shmid;
}

 2、shmat函数(关联)

功能:将共享内存段连接到进程地址空间

原型 :void *shmat(int shmid, const void *shmaddr, int shmflg);

参数: shmid: 共享内存标识 shmaddr:指定连接的地址 shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY

返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

这里我们要注意的是返回指针可以认为是共享内存的起始地址

 shmflg:

  1. SHM_RND:将 size 参数舍入到系统页面大小的倍数。这样做可以保证共享内存段的大小是系统页面大小的整数倍,以提高性能。

  2. SHM_RDONLY:以只读方式打开共享内存段。这意味着进程只能读取共享内存中的数据,不能进行写操作。这个标志通常用于让多个进程共享只读数据的情况

用法:

//返回指是共享内存的开始地址
void* attachShm(int shmid)
{void* start = shmat(shmid,nullptr,0);if((long long)start == -1L){std::cerr << errno << ":" << strerror(errno) <<std::endl;exit(3);}return start;
}

3、shmdt函数(去关联) 

功能:将共享内存段与当前进程脱离

原型: int shmdt(const void *shmaddr);

参数 :shmaddr: 由shmat所返回的指针

返回值:成功返回0;失败返回-1 注意:将共享内存段与当前进程脱离不等于删除共享内存段

用法:

void detachShm(void *start)
{if(shmdt(start) == -1){std::cerr <<"shmdt: "<< errno << ":" << strerror(errno) << std::endl;}
}

4、shmctl函数(控制)

功能:用于控制共享内存

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

参数:shmid:由shmget返回的共享内存标识码 cmd:将要采取的动作(有三个可取值) buf:指向一个保存着共享内存的模式状态和访问权限的数据结构

返回值:成功返回0;失败返回-1

这里我们重点关注:参数cmd

IPC_STAT:把shmid_ds结构中的数据设置为共享内存的当前关联值

IPC_SET:在进程有足够权限的前提下,把共享内存的当前值设置为shmid_ds数据结构中给出的值

IPC_RMID:删除共享内存

  struct shmid_ds *buf参数保存着共享内存的模式状态和访问权限的数据结构,里面存放这共享内存的共享参数

 三、用共享内存进行通信

下面我们将用共享内存实现server和client的通信

1、comm.hpp

我们定义一个comm.hpp的头文件定义server和client共同使用的函数:

#ifndef _COMM_HPP
#define _COMM_HPP#include<iostream>
#include<sys/types.h>
#include<sys/ipc.h>
#include<sys/shm.h>
#include<cerrno>
#include<cstring>
#include<cstdio>
#include<cstdlib>#define PATHNAME "."
#define PROJ_ID 0x66#define MAX_SIZE 4096//获取密钥k
key_t getKey()
{key_t k = ftok(PATHNAME,PROJ_ID);if(k < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(1);}return k;
}//调用shmget创建共享内存
int getShmHelper(key_t k, int flags)
{int shmid = shmget(k,MAX_SIZE,flags);if(shmid < 0){std::cerr << errno << ":" << strerror(errno) << std::endl;exit(2);}return shmid;
}//获取shmid标识码
int getShm(key_t k)
{return getShmHelper(k,IPC_CREAT);
}//调用getShmHelper
int createShm(key_t K)
{return getShmHelper(K,IPC_CREAT | IPC_EXCL | 0600);
}//返回指是共享内存的开始地址
void* attachShm(int shmid)
{void* start = shmat(shmid,nullptr,0);if((long long)start == -1L){std::cerr << errno << ":" << strerror(errno) <<std::endl;exit(3);}return start;
}//进行去关联
void detachShm(void *start)
{if(shmdt(start) == -1){std::cerr <<"shmdt: "<< errno << ":" << strerror(errno) << std::endl;}
}//删除共享内存
void delShm(int shmid)
{if(shmctl(shmid,IPC_RMID,nullptr) == -1){std::cerr << errno << ":" << strerror(errno) <<std::endl;}
}#endif

2、server.cpp 

这里我们让server进程,打印k和shmid值给我们看一下,并进行创建共享内存,并且进行和client进行通信

#include"comm.hpp"
#include <unistd.h>int main()
{key_t k = getKey();//看看kprintf("key: 0x%x\n",k);//创建共享内存int shmid = createShm(k);printf("shmid: %d\n",shmid);//关联共享内存char *start = (char*)attachShm(shmid);printf("attach success, address start: %p\n", start);const char* message = "hello server,我是clinet正在和你通信";pid_t id = getpid();int cnt = 1;while(true){sleep(5);snprintf(start,MAX_SIZE,"%s[pid:%d][信息标号:%d]",message,id,cnt);}detachShm(start);return 0;
}

 3、client.cpp

这里我们让client和server进行通信,server负责接受就可以了

#include"comm.hpp"
#include <unistd.h>int main()
{key_t k = getKey(); printf("key: 0x%x\n", k);int shmid = getShm(k);printf("shmid: %d\n", shmid);//关联共享内存char *start = (char*)attachShm(shmid);printf("attach success, address start: %p\n", start);const char* message = "hello server,我是clinet正在和你通信";pid_t id = getpid();int cnt = 1;while(true){sleep(5);snprintf(start,MAX_SIZE,"%s[pid:%d][信息标号:%d]",message,id,cnt);}//去关联detachShm(start);return 0;
}

4、现象

运行shm_server

运行shm_client 

 但是当我们第二次在运行./shm_server时候却不行了

这是为什么呢? 

我们通过ipcs -m命令查看一下:

ipcs -m

ipcs -m 是一个Unix/Linux系统中的命令,用于列出当前系统中的共享内存段信息。它可以显示已经创建的共享内存段的详细信息,包括标识符、权限、大小、进程ID等。

  • key:共享内存段的键值。
  • shmid:共享内存段的标识符。
  • owner:创建该共享内存段的用户ID。
  • perms:共享内存段的权限。
  • bytes:共享内存段的大小(字节数)。
  • nattch:连接到该共享内存段的进程数量。
  • status 字段表示共享内存段的状态

其中

status 值及其含义如下:

  • 0:表示共享内存段当前未被使用或已被释放。
  • dest:表示共享内存段标记为准备删除状态。这意味着共享内存段即将被销毁,但仍然有进程连接到它,只有当所有连接到该共享内存段的进程都脱离连接时,它才会被完全删除。
  • out:表示共享内存段处于被卸载状态。这意味着该共享内存段已经被脱离连接,但仍然存在于系统中。可以通过手动操作或进程退出来释放该共享内存段。
  • err:表示共享内存段状态异常,可能由于系统错误或其他问题导致无法正常访问和管理。

 

 这说明了共享内存生命周期是随操作系统的,不是随进程。用就是说如果我们没有主动调用shmcl函数去控制删除共享内存空间,那么我们后面就要自去删除一下就可以在次运行了。

ipcrm -m shmid

 

相关文章:

[Linux打怪升级之路]-system V共享内存

前言 作者&#xff1a;小蜗牛向前冲 名言&#xff1a;我可以接受失败&#xff0c;但我不能接受放弃 如果觉的博主的文章还不错的话&#xff0c;还请点赞&#xff0c;收藏&#xff0c;关注&#x1f440;支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 本期学习目标&…...

STM32不使用 cubeMX实现外部中断

这篇文章将介绍如何不使用 cubeMX完成外部中断的配置和实现。 文章目录 前言一、文件加入工程二、代码解析exti.cexti.hmain.c 注意&#xff1a;总结 前言 实验开发板&#xff1a;STM32F103C8T6。所需软件&#xff1a;keil5 &#xff0c; cubeMX 。实验目的&#xff1a;如何不…...

Nautilus Chain 与 Coin98 生态达成合作,加速 Zebec 生态亚洲战略进

目前&#xff0c;行业内首个模块化 Layer3 架构公链 Nautilus Chain 已经上线主网&#xff0c;揭示了模块化区块链领域迎来了全新的进程。在主网上线后&#xff0c;Nautilus Chain 将扮演 Zebec 生态中最重要的底层设施角色&#xff0c;并将为 Zebec APP 以及 Zebec Payroll 规…...

method.isAnnotationPresent(Xxx.class)一直为null

​​​​package com.dj.springtest.aspect;import com.dj.springtest.annotation.RequireRoles; import lombok.extern.slf4j.Slf4j; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.s…...

基于CNN实现谣言检测 - python 深度学习 机器学习 计算机竞赛

文章目录 1 前言1.1 背景 2 数据集3 实现过程4 CNN网络实现5 模型训练部分6 模型评估7 预测结果8 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于CNN实现谣言检测 该项目较为新颖&#xff0c;适合作为竞赛课题方向&#xff0c;学长非常推荐&am…...

MySQL——七、MySQL备份恢复

MySQL 一、MySQL日志管理1、MySQL日志类型2、错误日志3、通用查询日志4、慢查询日志5、二进制日志5.1 开启日志5.2 二进制日志的管理5.3 日志查看5.4 二进制日志还原数据 二、MySQL备份1、备份类型逻辑备份优缺点 2、备份内容3、备份工具3.1 MySQL自带的备份工具3.2 文件系统备…...

iOS如何实现语音转文字功能?

1.项目中添加权限 Privacy - Speech Recognition Usage Description : 需要语音识别权限才能实现语音转文字功能 2.添加头文件 #import <AVFoundation/AVFoundation.h> #import<Speech/Speech.h> 3.实现语音转文字逻辑: 3.1 根据wav语音文件创建请求 SFSpeechU…...

【下载器篇】获取微软应用商店应用安装包的方法

【下载器篇】获取微软应用商店应用安装包的方法 微软应用商店历史版本应用下载方法&#xff0c;部分历史版本无法搜索到—【蘇小沐】 文章目录 【下载器篇】获取微软应用商店应用安装包的方法1.实验环境 &#xff08;一&#xff09;微软商店的在线链接生成器1、复制该应用的在…...

云安全—集群攻击入口攻与防

0x00 前言 说到云安全肯定不能避免的是集群相关的内容&#xff0c;最出色的就是Kubernetes&#xff0c;也就是k8s。当然docker相关的内容也算是集群的一部分。但是docker容器本身的问题还是归属于容器本身。 0x01 概述 在集群攻击入口处的内容主要为&#xff1a; 应用安全恶…...

“传统”开发与AI开发的区别与联系(更新了GPT3.5的反馈)

1、传统开发的算法和软件整体&#xff0c;也可以看成是一个“大模型”&#xff0c;其中有不同层次的处理&#xff0c;最终能够完成从输入到输出的计算&#xff0c;不过&#xff0c;其中的计算都是人工定义的&#xff0c;一般依赖于研究成果的应用。研究成果在实际中的应用处理。…...

Unity 文字显示动画(2)

针对第一版的优化&#xff0c;自动适配文字大小&#xff0c;TextMeshPro可以拓展各种语言。第一版字母类语言效果更好。 using System.Collections; using System.Collections.Generic; using TMPro; using UnityEngine; using UnityEngine.UI;public partial class TextBeat…...

力扣每日一题53:最大子数组和

题目描述&#xff1a; 给你一个整数数组 nums &#xff0c;请你找出一个具有最大和的连续子数组&#xff08;子数组最少包含一个元素&#xff09;&#xff0c;返回其最大和。 子数组 是数组中的一个连续部分。 示例 1&#xff1a; 输入&#xff1a;nums [-2,1,-3,4,-1,2,1,…...

图论04-【无权无向】-图的广度优先遍历

文章目录 1. 代码仓库2. 广度优先遍历图解3.主要代码4. 完整代码 1. 代码仓库 https://github.com/Chufeng-Jiang/Graph-Theory 2. 广度优先遍历图解 3.主要代码 原点入队列原点出队列的同时&#xff0c;将与其相邻的顶点全部入队列下一个顶点出队列出队列的同时&#xff0c;将…...

layui的一些问题

为什么table.render, ins1.config有时候获取的值是上一次的?例如ins1.conf.page.curr? 这是一段table.render代码 let ins1 table.render({...})一般情况下ins1.conf可以获得表格的当前页,页数等;但是有时候获得的页数是上一次的;主要是因为在table.reload后没有继续赋值的…...

设计模式_中介者模式

中介者模式 介绍 设计模式定义案例问题堆积在哪里解决办法中介者代替了多个对象之间的互动 使对象1 2 3 之间的互动 变为&#xff1a; 对象1->中介 对象2->中介 对象3->中介好友之间 约饭好友1 通知 好友2 -3 -4 等等加一个群 谁想吃饭就 通知一下 类图 代码 角色 …...

062:mapboxGL通过jumpTo方式跳转到某位置

第062个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中通过jumpTo方式跳转到某位置。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共122行)相关API参考:专栏目标示例效果 配置方式 1)查看基础设置…...

学成在线第一天-课程内容管理服务搭建以及查询课程接口设计

目录 一、搭建课程内容管理服务 二、设计接口 三、面试题 四、总结 一、搭建课程内容管理服务 没什么好说的&#xff0c;直接就是创建内容模块 然后这个继承父模块&#xff0c;然后再课程内容模块下面创建三个子模块&#xff0c;model、sevice、controller model依赖base…...

4.7 IP多播

思维导图&#xff1a; **4.7.1 IP多播的基本概念** --- **1. 定义和背景** - IP多播&#xff1a;从一个源点发送信息至多个终点的技术。 - 1988年&#xff1a;Steve Deering首次提及IP多播。 - 1992年&#xff1a;IETF进行了首次IP多播试验&#xff0c;当时有20个网点参与。 …...

XML与html解析,区别,如何使用

目录 简介: HTML&#xff08;超文本标记语言&#xff09;&#xff1a; 如何使用HTML&#xff1a; XML&#xff08;可扩展标记语言&#xff09;&#xff1a; 如何使用XML&#xff1a; 区别&#xff1a; 简介: XML&#xff08;可扩展标记语言&#xff09;和 HTML&#xff…...

【广州华锐互动】利用VR开展建筑塔吊安全操作学习的好处?

随着科技的不断发展&#xff0c;虚拟现实&#xff08;VR&#xff09;技术已经逐渐渗透到各个领域&#xff0c;为人们的生活带来了前所未有的便利。在工程教育领域&#xff0c;VR建筑塔吊安全操作学习作为一种新型的教学手段&#xff0c;正逐渐成为提高教学质量和培养高素质工程…...

终极全面战争模组制作指南:5个步骤快速上手RPFM

终极全面战争模组制作指南&#xff1a;5个步骤快速上手RPFM 【免费下载链接】rpfm Rusted PackFile Manager (RPFM) is a... reimplementation in Rust and Qt5 of PackFile Manager (PFM), one of the best modding tools for Total War Games. 项目地址: https://gitcode.c…...

有没有国产的、不用写正则的监控工具?2026信创运维实战:实在Agent引领“零正则”监控新范式

进入2026年&#xff0c;企业数字化转型已全面步入“信创深水区”。随着国产操作系统、国产数据库及中间件的规模化铺设&#xff0c;运维团队面临的挑战正从“能不能用”转向“好不好用”。在这一背景下&#xff0c;寻找一款国产、免配置、尤其是“不用写正则表达式”的监控工具…...

别再死记硬背了!用Tarjan算法解决LeetCode 1192「关键连接」的保姆级思路拆解

从LeetCode 1192题实战拆解Tarjan算法&#xff1a;关键连接与图论面试精要 在分布式系统设计中&#xff0c;网络拓扑的稳定性直接决定了服务的可靠性。当某个数据中心的服务器集群出现连接故障时&#xff0c;如何快速识别出会导致网络分裂的关键线路&#xff1f;这道来自LeetCo…...

避开这些坑!用STC15单片机做超声波测距时,定时器溢出和温度补偿到底该怎么处理?

STC15单片机超声波测距实战&#xff1a;定时器溢出与温度补偿的深度优化方案 当超声波测距模块在STC15F2K60S2单片机上运行时&#xff0c;你是否遇到过这些情况&#xff1a;测量数据偶尔出现异常跳变&#xff0c;远距离测量结果不稳定&#xff0c;甚至程序莫名其妙进入死循环&a…...

RWKV7-1.5B-world惊艳效果:输入‘画一只猫’→文本生成→‘Describe in English’→专业动物学描述

RWKV7-1.5B-world惊艳效果&#xff1a;输入画一只猫→文本生成→Describe in English→专业动物学描述 1. 模型概述 RWKV7-1.5B-world是基于第7代RWKV架构的轻量级双语对话模型&#xff0c;拥有15亿参数。该模型采用创新的线性注意力机制替代传统Transformer的自回归结构&…...

告别繁琐!在Mac/Linux上为RuoYi-Vue集成自动化部署脚本的完整流程

告别繁琐&#xff01;在Mac/Linux上为RuoYi-Vue集成自动化部署脚本的完整流程 在快速迭代的现代开发环境中&#xff0c;手动执行重复性部署操作已成为效率瓶颈。对于使用RuoYi-Vue框架的开发者而言&#xff0c;每次代码生成后需要完成文件移动、数据库更新、项目编译等一系列操…...

d2dx:重塑经典暗黑2的现代游戏体验革新

d2dx&#xff1a;重塑经典暗黑2的现代游戏体验革新 【免费下载链接】d2dx D2DX is a complete solution to make Diablo II run well on modern PCs, with high fps and better resolutions. 项目地址: https://gitcode.com/gh_mirrors/d2/d2dx 在数字游戏遗产的复兴浪潮…...

I2C总线长距离传输解决方案与信号完整性优化

1. I2C总线长距离传输的挑战与解决方案在嵌入式系统和设备间通信中&#xff0c;I2C总线因其简单的两线制设计&#xff08;SCL时钟线和SDA数据线&#xff09;而广受欢迎。然而标准I2C协议最初设计用于板级短距离通信&#xff0c;当需要扩展到20米甚至更长距离时&#xff0c;会遇…...

从经典到现代:平板湍流边界层表面摩擦系数(Cf)公式的演进与应用指南

1. 平板湍流边界层表面摩擦系数的工程意义 想象一下你正在设计一架飞机的外形。机翼表面与空气的摩擦阻力会直接影响燃油效率和飞行性能&#xff0c;这个阻力的大小就与**表面摩擦系数&#xff08;Cf&#xff09;**密切相关。Cf是流体力学中一个看似简单却极其关键的参数&#…...

STM32F4上跑LVGL,用CubeMX+MDK从零搭建图形界面(附源码)

STM32F4与LVGL深度整合&#xff1a;从CubeMX配置到交互式UI实战 1. 现代嵌入式GUI开发的新范式 在智能硬件井喷式发展的今天&#xff0c;用户界面已成为产品差异化的关键因素。LVGL作为一款轻量级开源图形库&#xff0c;凭借其丰富的控件和硬件适配性&#xff0c;正逐渐成为嵌入…...