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 写入数据后,读端才开始读数据:

 

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/457413.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于SSM的BBS社区论坛系统源码

运行环境&#xff1a;ideamysql5.7jdk8maven 使用技术&#xff1a;ssmmysqlshirolayui 功能模块&#xff1a;用户管理、模板管理、帖子管理、公告管理、权限管理等

echarts:导入excel生成桑葚图

前言 前两天帮别人实现了一个小功能&#xff0c;主要是选择excel文件&#xff0c;读取里面的数据&#xff0c;将数据生成桑葚图 echarts官方桑葚图案例 实现 因为就是一个单纯的html文件&#xff0c;用到的库都是通过CDN的方式加载的&#xff0c;会有一些慢 <!DOCTYPE …

IPC 进程间通信 信号量集合 Linux环境 C语言实现

只用于多进程间的并发控制 一个信息量集合(信号量集 或 信息量数组)中顺序存储着多个信号量 相关接口函数&#xff1a; 实际项目&#xff0c;直接调用semop函数来作为某个或某几个信号量的PV操作函数会很不方便&#xff0c;因此会对信号量集合的接口进行二次封装&#xff0c;封…

AI实操Excel:在Excel中学习人工智能基础算法

人工智能&#xff08;AI&#xff09;已经成为当今科技领域最热门的话题之一&#xff0c;但对于初学者来说&#xff0c;直接上手复杂的编程和算法可能会感到有些困难。今天&#xff0c;我要向大家介绍一个非常实用的工具——AI实操Excel&#xff0c;这是一个通过Excel实现人工智…

「二叉树进阶题解:构建、遍历与结构转化全解析」

文章目录 根据二叉树创建字符串思路代码 二叉树的层序遍历思路代码 二叉树的最近公共祖先思路代码 二叉搜索树与双向链表思路代码 从前序与中序遍历序列构造二叉树思路代码 总结 根据二叉树创建字符串 题目&#xff1a; 样例&#xff1a; 可以看见&#xff0c;唯一特殊的就…

影刀RPA实战:常见实用功能指令

1. 电脑锁屏与解屏 在实际工作中&#xff0c;我们为了自身工作电脑数据文件的安全&#xff0c;都会为电脑设置密码&#xff0c;当我们离开电脑时&#xff0c;直接锁屏&#xff0c;即使不手动锁屏&#xff0c;也会在一定时间内自动锁屏。 如果你的工作是影刀RPA帮你自动化处理…

Spring Boot驱动的厨艺社交平台设计与实现

5 系统实现 5.1食材分类管理 管理员管理食材分类&#xff0c;可以添加&#xff0c;修改&#xff0c;删除食材分类信息。下图就是食材分类管理页面。 图5.1 食材分类管理页面 5.2 用户信息管理 管理员管理用户信息&#xff0c;可以添加&#xff0c;修改&#xff0c;删除用户信…

kafka 分布式(不是单机)的情况下,如何保证消息的顺序消费?

大家好&#xff0c;我是锋哥。今天分享关于【kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的顺序消费?】面试题&#xff1f;希望对大家有帮助&#xff1b; kafka 分布式&#xff08;不是单机&#xff09;的情况下&#xff0c;如何保证消息的…

量子变分算法 (python qiskit)

背景 变分量子算法是用于观察嘈杂的近期设备上的量子计算效用的有前途的候选混合算法。变分算法的特点是使用经典优化算法迭代更新参数化试验解决方案或“拟设”。这些方法中最重要的是变分量子特征求解器 (VQE)&#xff0c;它旨在求解给定汉密尔顿量的基态&#xff0c;该汉密尔…

mac 上使用 cmake 构建包含 OpenMP 的项目

安装依赖 # clang 默认不支持 -fopenmp&#xff0c;因为它没有内置 OpenMP 支持。 # 为了解决这个问题&#xff0c;需要安装 libomp 并配置 clang 使用 libomp brew install libomp# macOS 自带的 clang 编译器被修改过&#xff0c;默认禁用了 OpenMP&#xff0c; # 而不支持 …

【K8S系列】Kubernetes Service 基础知识 详细介绍

在 Kubernetes 中&#xff0c;Service 是一种抽象的资源&#xff0c;用于定义一组 Pod 的访问策略。它为这些 Pod 提供了一个稳定的访问入口&#xff0c;解决了 Pod 可能频繁变化的问题。本文将详细介绍 Kubernetes Service 的类型、功能、使用场景、DNS 和负载均衡等方面。 1.…

class 36 二叉树高频题目 - 上 (不含有树形dp)

1. BFS 的两种方式 如下图, 是一个二叉树. 我们需要按照层的方式来遍历这棵树. 1.1 使用 JDK 自带的类实现(链表实现, 经典 BFS) 首先我们实现一个队列, 这个队列从头进, 从尾出.然后将根节点放入其中, 然后将放入的节点弹出,然后继续验证弹出的节点有没有左孩子, 若是有, 将…

【HTML】之form表单元素详解

HTML表单是网页与用户交互的关键组成部分&#xff0c;它允许用户输入数据并将数据提交到服务器进行处理。本文将全面详细地介绍HTML表单的各个方面&#xff0c;从基础元素到高级用法&#xff0c;并提供丰富的代码示例和中文注释&#xff0c;帮助你彻底掌握表单的使用。 1. 表单…

强大!Spring Boot 3.3 集成 PDFBox 轻松实现电子签章功能!

强大&#xff01;Spring Boot 3.3 集成 PDFBox 轻松实现电子签章功能&#xff01; 随着数字化办公和电子合同的普及&#xff0c;PDF 文档已经成为很多业务场景中的标准文件格式。为了确保文档的安全性和法律效力&#xff0c;电子签章技术应运而生。电子签章不仅可以证明文件的…

视频美颜平台的搭建指南:基于直播美颜SDK的完整解决方案

众所周知&#xff0c;直播美颜SDK是实现视频美颜功能的核心。本文将详细解析如何基于直播美颜SDK搭建一个完整的视频美颜平台。 一、视频美颜SDK的核心功能 直播美颜SDK作为平台的技术核心&#xff0c;能够提供丰富的美颜效果和稳定的视频处理能力。通常&#xff0c;SDK具备以…

传输层TCP

报头 1.报头和有效载荷如何分离将&#xff0c;有效载荷向上交付&#xff1f; tcp有个标准报头长度为20&#xff0c;那是不是以为我们可以像udp一样分离依靠报头大小去分离&#xff0c;我们仔细去看我们报头中还有个选项没包含到。 我们还有个首部长度&#xff0c;四位可以表…

【Axure高保真原型】分级树筛选中继器表格

今天和大家分享分级树筛选中继器表格的原型模板&#xff0c;点击树的箭头可以展开或者收起子级内容&#xff0c;点击内容&#xff0c;可以筛选出该内容及子级内容下所有的表格数据。左侧的树和右侧的表格都是用中继器制作的&#xff0c;所以使用也很方便&#xff0c;只需要在中…

SwiftUI:单个App支持设置多语言

SwiftUI 全新多语言方案 简化本地化的字符串- WWDC21 - 视频 本地化您的SwiftUI app - WWDC21 - 视频 构建全球化App&#xff1a;本地化的示例- WWDC22 - 视频 构建支持多语言的App - WWDC24 - 视频 单个App支持设置多语言 工程 Info.plist里添加 键值UIPrefersShowingLangua…

论1+2+3+4+... = -1/12 的不同算法

我们熟知自然数全加和&#xff0c; 推导过程如下&#xff0c; 这个解法并不难&#xff0c;非常容易看懂&#xff0c;但是并不容易真正理解。正负交错和无穷项计算&#xff0c;只需要保持方程的形态&#xff0c;就可以“预知”结果。但是这到底说的是什么意思&#xff1f;比如和…

【AI换装整合及教程】CatVTON:时尚与科技的完美融合

在当今数字化时代&#xff0c;时尚行业正经历着一场前所未有的变革&#xff0c;而 CatVTON 作为一款由中山大学、Pixocial 等机构联合研发的轻量化 AI 虚拟换装工具&#xff0c;无疑是这场变革中的璀璨明星。 一、独特的技术架构 CatVTON 基于 Stable Diffusion v1.5 inpainit…