Linux -- 共享内存(2)
目录
命令 ipcs -m :
命令 ipcrm -m shmid:
共享内存的通信:
为什么共享内存更高效?
代码:
ShmClient.cc:
ShmServer.cc:
结果:
如何让共享内存实现同步?
代码:
Comm.hpp
Fifo.hpp
ShmClient.cc
ShmServer.cc
结果:
接上篇的共享内存的函数封装。
当两个不同的进程 server 和 client 的 GetShmKeyOrDie 函数传了一样的 pathname 和 pro_jid 时,ftok 就会给两个不同的进程返回一样的 key 值!即两个不同的进程看到同一个共享内存!
命令 ipcs -m :
命令 ipcs -m 可以查看共享内存的相关信息,如下图:
perms 是共享内存的权限。
- 当我们调用 GetShm 函数时,由于没有设置权限,故创建出来的共享内存的权限为 0(如上图);
- 当我们调用 CreateShm 函数时,由于设置的权限为 0666,故创建出来的共享内存的权限为 666(如下图)。
bytes 是共享内存的大小,单位为字节,共享内存的大小是用户定义的。
nattch 是共享内存挂接的进程的个数。
我们可以写个代码验证一下:我们创建出共享内存后休眠 2s,然后把共享内存挂接到进程中,再次休眠 5s,休眠结束后把共享内存从进程的地址空间中分离出来,注意只是分离出来,并没有删除共享内存。
#include"Comm.hpp"
#include<unistd.h>
int main()
{key_t k=GetShmKeyOrDie(path,pro_jid);cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出int shmid=CreateShm(k,defaultsize);cout<<" shmid:"<<shmid<<endl;sleep(2);char* addr=(char*)ShmAttach(shmid);cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;sleep(5);ShmDetach(addr);cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;return 0;
}
用 while :;do ipcs -m;sleep 1;done 命令时时监控共享内存的信息:
命令 ipcrm -m shmid:
除了调用函数删除,还可以用命令 ipcrm -m shmid 来删除共享内存,如下图,删除完再次查看共享内存的信息时,已经查不到了:
共享内存的通信:
创建出共享内存之后,我们就可以通信了。
共享内存可以直接写入,不需要像管道一样调用系统调用,因为它本质上是一块内存区域,这块内存区域由操作系统映射到多个进程的地址空间中。当一个进程修改了这块共享内存中的数据时,其他进程可以立即看到这些修改,因为它们共享的是同一块物理内存。这提高了通信的效率。
为什么共享内存更高效?
共享内存之所以被认为是一种高效的进程间通信(IPC)方式,主要是因为它避免了数据复制和频繁的系统调用。以下是具体原因:
- 减少数据拷贝:在使用共享内存时,数据只需要在一个地方修改即可被所有有权访问该内存段的进程看到。相比之下,其他IPC机制如管道(pipe)等通常需要将数据从发送者的用户空间复制到内核空间,然后再从内核空间复制到接收者的用户空间。这种多次的数据拷贝过程会消耗额外的时间和CPU资源。
- 直接内存访问:共享内存使得进程可以直接对内存进行读写操作,就像操作自己的私有内存一样。这种直接访问的方式减少了中间环节,提高了数据传输的速度。
代码:
ShmClient.cc:
#include"Comm.hpp"
#include<unistd.h>int main()
{key_t k=GetShmKeyOrDie(path,pro_jid);cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出int shmid=GetShm(k,defaultsize);cout<<" shmid:"<<shmid<<endl;char* addr=(char*)ShmAttach(shmid);cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;memset(addr,0,defaultsize);//初始化共享内存for(char ch='A';ch<='Z';ch++)//写入{addr[ch-'A']=ch;sleep(1);}ShmDetach(addr);cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;return 0;
}
ShmServer.cc:
#include"Comm.hpp"
#include<unistd.h>
int main()
{key_t k=GetShmKeyOrDie(path,pro_jid);cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出int shmid=CreateShm(k,defaultsize);cout<<" shmid:"<<shmid<<endl;char* addr=(char*)ShmAttach(shmid);cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;//读取数据for(;;){cout<<" shm content:"<<addr<<endl;sleep(1);}ShmDetach(addr);cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;DeleteShm(shmid);return 0;
}
结果:
可以看出,写端 client 还没有向共享内存写入数据,但读端 server 已经在读取了,即读端没有阻塞等待写端写入数据!也就是说共享内存没有提供协同机制!这将导致数据不一致!
如何让共享内存实现同步?
我们利用管道来实现同步。
我们并不是将要写入共享内存的数据写入管道中,而是设置一个管道,server 使用管道的读端, client 使用管道的写端。
共享内存的读端 server 因为管道中还没有写入数据,就阻塞等待,等待管道中的数据,等到了管道的数据才可以读取共享内存的数据,而共享内存的写端 client 写完数据后,向管道写入一个数据,server 的管道读端读到了这个数据,结束阻塞,server就可以读取共享内存的数据了。
代码:
Comm.hpp
#pragma once
#include <iostream>
#include <sys/ipc.h>
#include <cstdlib>
#include <cerrno>
#include <string>
#include <cstring>
#include <sys/shm.h>
#include <sys/types.h>
using namespace std;const char *path = "./shm_test";
const int pro_jid = 0x66;
const int defaultsize = 4096;key_t GetShmKeyOrDie(const char *pathname, const int pro_jid)
{key_t k = ftok(pathname, pro_jid); // 得到key值if (k < 0)// 获取失败{cerr << " ftok failed,errno:" << errno << ", errstring:" << strerror(errno) << endl;exit(1); // 直接终止程序}return k; // 获取成功
}int CreateShmOrDie(key_t key, int size, int flag)
{// 得到共享内存的shmidint shmid = shmget(key, size, flag);if (shmid < 0) // 创建失败{cerr << " shmget failed, errno:" << errno << ", errstring:" << strerror(errno) << endl;exit(2);}return shmid; // 创建成功
}int CreateShm(key_t key, int size)
{// 创建一个新的共享内存,并设置权限return CreateShmOrDie(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int GetShm(key_t key, int size)
{return CreateShmOrDie(key, size, IPC_CREAT);
}void DeleteShm(int shmid)
{int n = shmctl(shmid, IPC_RMID, nullptr);if (n < 0)cerr << " Delete failed,errno:" << errno << ", errstring:" << strerror(errno) << endl;elsecout << " Delete success, shmid:" << shmid << endl;
}string ToHex(key_t k)//将 key 值转为十六进制
{char buffer[1024];//用C语言方便用 %x 直接转为十六进制snprintf(buffer,sizeof(buffer),"0x%x",k);return buffer;
}void DebugShm(int shmid)
{struct shmid_ds shmds;int n = shmctl(shmid, IPC_STAT, &shmds);if (n < 0)cerr << " shmctl failed " << endl;else{std::cout << "shmds.shm_segsz: " << shmds.shm_segsz << std::endl;//共享内存的大小std::cout << "shmds.shm_nattch:" << shmds.shm_nattch << std::endl;//有多少个进程挂接std::cout << "shmds.shm_ctime:" << shmds.shm_ctime << std::endl;//上一次挂接或取消挂接的时间std::cout << "shmds.shm_perm.__key:" << ToHex(shmds.shm_perm.__key) << std::endl;//对应的key}
}void *ShmAttach(int shmid)
{void* addr=shmat(shmid,nullptr,0);if((long long int)addr==-1){//挂接失败cerr<<" attach failed,errno:"<<errno<<", errstring:"<<strerror(errno)<<endl;return nullptr;}else{//挂接成功return addr;}
}void ShmDetach(void *addr)
{int n=shmdt(addr);if(n<0){cerr<<" Detach failed,errno:"<<errno<<", errstring:"<<strerror(errno)<<endl;}
}
Fifo.hpp
#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <cassert>using namespace std;#define Mode 0666
#define Path "./fifo"class Fifo
{
public:Fifo(const string &path = Path) : _path(path){umask(0);int n = mkfifo(_path.c_str(), Mode);if (n == 0){cout << "mkfifo success" << endl;}else{cerr << "mkfifo failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}~Fifo(){int n = unlink(_path.c_str());if (n == 0){cout << "remove fifo file " << _path << " success" << endl;}else{cerr << "remove failed, errno: " << errno << ", errstring: " << strerror(errno) << endl;}}private:string _path; // 文件路径+文件名
};class Sync
{
public:Sync():rfd(-1),wfd(-1){ }void OpenReadOrDie(){rfd=open(Path,O_RDONLY);if(rfd<0)exit(1);}void OpenWriteOrDie(){wfd=open(Path,O_WRONLY);if(wfd<0)exit(1);}bool Wait(){//读端bool ret=true;uint32_t c=0;ssize_t n=read(rfd,&c,sizeof(uint32_t));if(n==sizeof(uint32_t)){cout<<" server wakeup, begin read shm..." << endl;}else if(n==0){return false;}else{return false;}return true;}void Wakeup(){//写端uint32_t c=0;ssize_t n=write(wfd,&c,sizeof(c));assert(n==sizeof(uint32_t));cout<<" Wakeup server "<<endl;}~Sync(){ }
private:int rfd;int wfd;
};#endif
ShmClient.cc
#include"Comm.hpp"
#include"Fifo.hpp"
#include<unistd.h>int main()
{key_t k=GetShmKeyOrDie(path,pro_jid);cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出int shmid=GetShm(k,defaultsize);cout<<" shmid:"<<shmid<<endl;char* addr=(char*)ShmAttach(shmid);cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;Sync sync;sync.OpenWriteOrDie();//打开写端memset(addr,0,defaultsize);//初始化共享内存sleep(5);for(char ch='A';ch<='Z';ch++)//写入{addr[ch-'A']=ch;sleep(1);sync.Wakeup();//写完了}ShmDetach(addr);cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;return 0;
}
ShmServer.cc
#include"Comm.hpp"
#include"Fifo.hpp"
#include<unistd.h>
int main()
{key_t k=GetShmKeyOrDie(path,pro_jid);cout<<" key:"<<ToHex(k)<<endl;//转为十六进制输出int shmid=CreateShm(k,defaultsize);cout<<" shmid:"<<shmid<<endl;char* addr=(char*)ShmAttach(shmid);cout<<" Attach success, addr:"<<ToHex((uint64_t)addr)<<endl;Fifo fifo;Sync sync;sync.OpenReadOrDie();//打开读端//读取数据for(;;){if(!sync.Wait()) break;//写端写完了,读端可以读了cout<<" shm content:"<<addr<<endl;sleep(1);}ShmDetach(addr);cout<<" Detach success "<<ToHex((uint64_t)addr)<<endl;DeleteShm(shmid);return 0;
}
结果:
server 运行,并没有向之前一样直接读数据,而是等待写端写入数据:
client 写入数据后,读端才开始读数据:
相关文章:
Linux -- 共享内存(2)
目录 命令 ipcs -m : 命令 ipcrm -m shmid: 共享内存的通信: 为什么共享内存更高效? 代码: ShmClient.cc: ShmServer.cc: 结果: 如何让共享内存实现同步? 代码&a…...
云函数实现发送邮件,以qq邮箱为例
云函数实现发送邮件,前端传参调用发送邮件即可。以qq邮箱为例。 1、开启qq邮箱的smtp服务并且生成授权码,操作界面如下图: 2、在腾讯云新建一个云函数代码如下: const nodemailer require("nodemailer");// 云函数入口函数 export…...
Kafka如何控制消费的位置?
大家好,我是锋哥。今天分享关于【Kafka如何控制消费的位置?】面试题?希望对大家有帮助; Kafka如何控制消费的位置? 1000道 互联网大厂Java工程师 精选面试题-Java资源分享网 在 Kafka 中,控制消费位置主要通过以下几个机制来实…...
python爬虫——Selenium的基本使用
目录 一、Selenium的介绍 二、环境准备 1.安装Selenium 2.安装WebDriver 三、元素定位 1.常用定位元素的方法 2. 通过指定方式定位元素 四、窗口操作 1.最大化浏览器窗口 2.设置浏览器窗口大小 3.切换窗口或标签页 切换回主窗口 4. 关闭窗口 关闭当前窗口 关闭所…...
【Linux】【xmake】安装 + C/C++常用项目配置
文章目录 0. 环境准备1. 子命令create - 快速创建项目build - 构建程序config - 配置编译需要的参数show - 查看当前工程基本信息update - 程序自更新 2. C/C 项目常用配置2.1 项目目标类型2.2 添加宏定义2.3 头文件路径和链接库配置2.4 设置语言标准2.5 设置编译优化2.6 添加源…...
Android 添加菜单开关控制Camera相机和第三方相机
本文主要通过SystemProperties系统属性和Settings.System存储数据库的状态进行判断,从而实现控制相机 /vendor/mediatek/proprietary/packages/apps/MtkSettings/res/values-zh-rCN/strings.xml <!--camera--> <string name="manager_camera_switch"&…...
【Java知识】使用jacoco实现代码覆盖率测试
文章目录 1. 添加JaCoCo插件到项目2. 配置Maven Surefire Plugin3. 执行测试并生成报告4. 查看覆盖率报告注意事项 要使用JaCoCo实现代码覆盖率测试,你需要遵循以下步骤: 1. 添加JaCoCo插件到项目 在Maven项目的pom.xml文件中添加JaCoCo插件。这允许你执…...
道路车辆功能安全 ISO 26262标准(9-2)—面向汽车安全完整性等级 (ASIL) 和安全的分析
写在前面 本系列文章主要讲解道路车辆功能安全ISO26262标准的相关知识,希望能帮助更多的同学认识和了解功能安全标准。 若有相关问题,欢迎评论沟通,共同进步。(*^▽^*) 1. 道路车辆功能安全ISO 26262标准 9. ISO 26262-9 面向汽车安全完整…...
hutool常用方法
1、树结构工具-TreeUtil 构建Tree示例 package com.sl.transport.common.util;import cn.hutool.core.bean.BeanUtil; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.lang.tree.Tree; import cn.hutool.core.lang.tree.TreeNode; import cn.hutool.core…...
CloudSat数据产品数据下载与处理 (matlab)
CloudSat数据下载 这个数据我之前和CALIPSO弄混了,后来发现它们虽然是同一个火箭上去,但是数据产品却在不同的平台下,CloudSat的数据更加关注云的特性,包括云覆盖、云水当量、云分类数据。 数据网址在:CloudSat网址 …...
LDR6500 一拖三快充线的定义与特点
定义:LDR6500 一拖三快充线是一种具有 Type-C 接口的充电线,它的最大特点是可以同时连接三个设备进行快速充电。 特点: 高效充电:采用先进的快充技术,能够快速为设备充电,大大缩短充电时间。同时…...
Elasticsearch安装使用
ES 概述 Elasticsearch,简称为 ES,是一款非常强大的开源的高扩展的分布式全文检索引擎,可以帮助我们从海量数据中快速找到需要的内容,它可以近乎实时的存储、检索数据.还可以可以实现日志统计、分析、系统监控等功能. 官网:https://www.elast…...
计算机网络的主要知识点小结
计算机网络是指将多台计算机通过通信线路连接起来,实现资源共享和信息传递的系统。 一、计算机网络概述 1. 定义和功能 - 定义:计算机网络是将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操…...
fastjson/jackson对getter,setter和constructor的区分
在复现完fastjson1.2.24-1.2.80和jackson的所有相关漏洞后,总结的一些规则 以下均指对json的反序列化过程 setter fastjson调用setter:遍历所有方法,找出所有满足setter要求的方法,再根据传入的json去反射调用 jackson调用set…...
认识CSS语法
CSS(网页美容) 重点:选择器、盒子模型、浮动、定位、动画,伸缩布局 Css的作用: 美化网页:CSS控制标签的样式 网页布局:CSS控制标签的位置 概念:层叠样式表(级联样式表…...
Linux运维篇-ansible的使用
目录 ansible简介ansible架构1、连接插件2、核心模块3、自定义模块4、插件5、剧本6、主机清单 ansible的执行过程安装Ansibleansible的使用ansible.cfg文件修改添加主机清单方式一方式二方式三 测试主机清单连接 ansible简介 简单来说,ansible就是一个自动化运维工…...
【MySQL】日志
1. 日志基本了解 常见的MySQL Server日志类型,以及记录的日志信息(场景通俗理解) 错误日志 记录的主要信息由服务器关闭、启动、崩溃事件;MySQL运行过程中出现的错误、警告和严重事件以及与权限、配置相关的问题使用场景 诊断MyS…...
2024年CentOS镜像下载地址,包括CentOS官网、国内镜像下载,超详细也
这里给大家提供了4种镜像下载地址,包括CentOS官方镜像下载、阿里云开源镜像站下载、网易开源镜像下载搜狐开源镜像下载。 1.CentOS官网镜像下载 因为服务器在国外所以打开CentOS官方网站的时候可能会比较慢。大家可以选择后面几种国内镜像下载方式。 1.1进入CentO…...
STL学习-顺序容器-array数组
array模板类是C11引入。它是有着固定大小用于保存一系列同类型元素的顺序容容器,因此不能对它进行增加或者删除,只能使用或者替换它的元素值。 1.定义及初始化 array定义对象时,需要传入类型和大小,且大小不能修改。array是唯--个如果不初始化,它的初始化是不明确…...
Spring Boot框架下的酒店住宿登记系统
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...
electron展示下载进度条
我们使用electron下载文件时,会发现不像浏览器一样会有地方展示下载进度,这导致下载一些大文件时不知道下载进度到哪里了 下面我们通过electron提供的will-download监听和element-plus中的ElNotification和ElProgress组件实现这一功能 实现逻辑 触发…...
Spark 基础操作
Spark 操作 创建操作(Creation Operation) 用于RDD创建工作。RDD创建只有两种方法,一种是来自于内存集合和外部存储系统,另一种是通过转换操作生成的RDD 转换操作(Transformation Operation) 将RDD通过一定的操作变成新的RDD,比如HadoopR…...
VoLTE 微案例:VoLTE 注册失败,I-CSCF 返回 403,HSS(UAR) 返回 5001
目录 1. 问题描述 2. 故障注册流程与正常流程对照 3. 结论 博主wx:yuanlai45_csdn 博主qq:2777137742 想要 深入学习 5GC IMS 等通信知识(加入 51学通信),或者想要 cpp 方向修改简历,模拟面试,学习指导都可以添加博主低价指导哈。 1. 问题描述...
智能财务 | 数据与融合,激发企业财务数智化转型思考
数据与融合,激发企业财务数智化转型思考 用友持续深耕企业财务领域,见证中国企业走过了财务电算化、信息化时代,当下共同经历数智化时代。2023 年度,通过走访标杆企业,与高校教授、权威机构学者共同探讨等形式…...
docker 下载netcore 镜像
dotnet-docker/README.runtime.md at main dotnet/dotnet-docker GitHub docker pull mcr.microsoft.com/dotnet/runtime:8.0 docker pull mcr.microsoft.com/dotnet/runtime:3.1...
Ajax:请求 响应
Ajax:请求 & 响应 AjaxjQuery的Ajax接口$.get$.post$.ajax PostMan 接口测试getpost Ajax 浏览器中看到的数据,并不是保存在浏览器本地的,而是实时向服务器进行请求的。当服务器接收到请求,就会发回一个响应,此时浏…...
WebForms DataList 控件深入解析
WebForms DataList 控件深入解析 概述 在 ASP.NET WebForms 的众多服务器控件中,DataList 控件是一个功能强大的数据绑定控件,它允许开发者以表格形式展示和操作数据。DataList 控件类似于 Repeater 控件,但提供了更多的内置布局和样式选项…...
【有啥问啥】DINO:一种改进的去噪锚框的端到端目标检测器
DINO:一种改进的去噪锚框的端到端目标检测器 在目标检测领域,DINO(DETR with Improved DeNoising Anchor Boxes for End-to-End Object Detection)是一种创新的端到端目标检测模型,旨在解决传统目标检测算法中的一些关…...
自由学习记录(15)
Java注解 else if的省略问题(可能看花) else if也是取最近的if连通,看上去加了{}就可以正常执行了,缩进要命,不提示真容易看错, 组合数公式和数组参数 在 C 中,数组作为函数参数时,…...
Docker 部署 JDK11 图文并茂简单易懂
部署 JDK11 ( Docker ) [Step 1] : 下载JDK11 - JDK 11 | Oracle 甲骨文官网 [Step 2] : jdk11上传服务器/root/jdk11 可自行创建文件夹 进入目录 /root/jdk11 解压文件 tar -zxvf jdk-11.0.22_linux-x64_bin.tar.gz解压后 进入 /root/jdk11/jdk-11.0.22 创建 jre 文件 ./bi…...
动物摄影网站/有免费做网站的吗
1. 更新不透明贴图的压缩格式为ETC 4bit,因为android市场的手机中的GPU有多种,每家的GPU支持不同的压缩格式,但他们都兼容ETC格式, 2. 对于透明贴图,我们只能选择RGBA 16bit 或者RGBA 32bit。 3. 减少FPS&#x…...
网站要去公安局备案吗/湘潭关键词优化公司
mysql数据类型,varchar int char boolean text long double float一.查询!查询语句语法:select 列名 from 表名!条件查询:关键字 where 开始,后边的就是查询条件!having筛选!like 模糊查询 like…...
三合一网站建设平台/百度一下首页登录
评分说明:(1)只要认真完成作业(无论对错),即可得到基础分8分,总分为22分;(2)如果总得分超过基础分(8分),则按如下参考答案及评分标准逐项给分,并累加核算本题总得分。参考答案及评分标准:(1)R1的路由表&…...
广州企业网站制作推广运营/新手怎样做网络推广
两个datetime进行时差计算报错: TypeError: cant subtract offset-naive and offset-aware datetimes原来是两个相减的时间时区不一致 # -*- coding: utf-8 -*-from datetime import datetime import pytznow1 datetime.now(tzpytz.UTC) print(now1) # 零时区的时…...
做公司 网站建设/域名服务器地址查询
前端学习笔记教程不定期更新中,传送门: 前端HTML第一天:什么是网页?什么是HTML?网页怎么形成?黑马程序员前端-CSS入门总结黑马程序员前端-CSS之emmet语法黑马程序员前端-CSS的复合选择器黑马程序员前端-CS…...
重庆市娱乐场所暂停营业/西安网络优化培训机构公司
文章目录1 SpringMVC1.1 SpringMVC概念1.2 什么是MVC1.3 SpringMVC架构(MVC设计模式在BS系统下的应用)1.4 案列:展示汽车数据1.4.1 需求1.4.2 创建Maven Module项目1.4.2 创建相关包1.4.3 创建Java项目1.创建程序运行类RunApp12.创建汽车Car类…...