【Linux—进程间通信】共享内存的原理、创建及使用

什么是共享内存

      共享内存是一种计算机编程中的技术,它允许多个进程访问同一块内存区域,以此作为进程间通信(IPC, Inter-Process Communication)的一种方式。这种方式相对于管道、套接字等通信手段,具有更高的效率,因为数据不需要在用户空间和内核空间之间进行复制,也不需要经过序列化和反序列化的复杂过程。

特点:

  • 高速度:由于省去了数据复制和上下文切换的开销,共享内存提供了非常高的数据交换速度。
  • 低延迟:适用于需要快速响应和大数据量传输的场景。
  • 同步需求:虽然高效,但多个进程同时访问同一块内存可能会导致数据不一致。因此,需要使用如互斥锁、信号量等同步工具来确保数据的正确性和完整性。
  • 生命周期管理:共享内存段需要显式创建、映射到进程地址空间、使用后断开连接,并在不再需要时销毁,以避免资源泄露。
  • 共享内存在系统中可以存在多个,供不同进程之间进行通信

共享内存的原理

每一个进程都有属于自己的进程地址空间,假设操作系统在物理内存开辟了一段空间,该进程可以创建一段虚拟内存,将这段虚拟内存的起始与结束地址通过页表与物理内存的空间构建联系

如果另一个进程,也通过上述方式,通过页表映射到同一段物理内存,那就实现了让多个进程看到同一段空间,这样当一个进程向这段物理空间写入数据,另一个进程就可以马上从这段空间读取数据了,就可以实现进程间的通信了

共享内存的使用

(一)创建共享内存

#原型int shmget(key_t key, size_t size, int shmflg);
#参数key:用户自定义共享内存的标识size:共享内存大小shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的
#返回值:成功返回一个非负整数,即该共享内存段的标识码;失败返回-1

【解释】:

# key:由于进程间具有独立性,所以共享内存一定不是某一个进程自己创建的,而是进程通过函数调用让操作系统创建的,而为了使另一个进程可以找到该共享内存,每一个共享内存一定有一个唯一性的标识,但是这个标识一定不能是操作系统自己独立生成的,因为这样只有要创建共享内存的那个进程能找到该共享内存。所以用户可以通过key自己设定唯一的标识,key一般使用函数调用生成

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);

pathname为文件路径,proj_id为项目id,这两个都是由用户自己设定的,该函数会通过特定的算法将参数形成唯一的key值

#size:共享内存的大小,建议设置为4096个字节的倍数,例如:当我们将共享内存设置为4097个字节,OS会申请4096*2大小的空间,由于我们申请的是4097个字节,剩下的4095个字节我们不能使用,就会浪费掉

#shmflg: 标志位参数有两种:IPC_CREAT、IPC_EXCL,常用的反方式有两种

  • IPC_CREAT: 如果要创建的共享内存不存在那就创建,如果存在就返回该共享内存
  • IPC_CREAT | IPC_EXCL:如果创建的共享内存不存在那就创建,如果存在就报错
  • 在使用时后面一般还要加上权限,防止进程无法与共享内存联系(注意)

ps:第二种使用方法可以保证每次创建的共享内存都是新创建的,所以在使用上,IPC_CREAT | IPC_EXCL一般用于创建共享内存,IPC_CREAT一般用于获取共享内存

#返回值:共享内存创建成功就返回该共享内存的shmid,失败就返回-1

key和shmid都是标识共享内存的唯一性字段,不过key是用户自定义的,用于让内核区分shm唯一性的,用户不能通过key进行对shm管理,而shmid是有内核返回的一个值,是让用户对共享内存进行管理的id值

(二)删除共享内存

  • 查看所有的共享内存:
ipcs -m

  • 利用指令删除共享内存:
ipcrm -m shmid

  • 代码删除共享内存:

shmctl函数

#功能:用于控制共享内存
#原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
#参数:shmid:由shmget返回的共享内存标识码cmd:将要采取的动作(有三个可取值)buf:指向一个保存着共享内存的模式状态和访问权限的数据结构
#返回值:成功返回0;失败返回-1
命令说明
IPC_STAT把shmid_ds数据结构中的数据设置为共享内存当前关联值
IPC_SET在进程有足够权限的前提下,把共享内存当前关联值设置为shmid_ds数据结构中给出的值
IPC_RMID删除共享内存段

ps.删除共享内存关心第三个参数,可以直接把他设置为nullptr

(三)将共享内存连接到进程地址空间

shmat函数

#功能:将共享内存段连接到进程地址空间
#原型void *shmat(int shmid, const void *shmaddr, int shmflg);
#参数shmid: 共享内存标识shmaddr:指定连接的地址shmflg:它的两个可能取值是SHM_RND和SHM_RDONLY
#返回值:成功返回一个指针,指向共享内存第一个节;失败返回-1

(四)将共享内存从进程地址空间脱离

#功能:将共享内存段与当前进程脱离
#原型int shmdt(const void *shmaddr);
#参数shmaddr: 由shmat所返回的指针
#返回值:成功返回0;失败返回-1

注意:将共享内存段与当前进程脱离不等于删除共享内存段


补充:共享内存不具有进程之间的同步机制,假设一个进程负责读,一个进程负责写,就算共享内存中没有写入数据,读进程还是会一直读,这就可能会发生写进程才写了一半的数据就被读走了,造成数据不一致问题。我们可以利用管道解决这个问题,因为管道具有同步机制,我们让写端写完以后,通过管道传输信号,只有读端通过管道接受到信号以后,才会进行对共享内存的读取

(向管道中写的信号是什么不重要,只要向共享内存中写后,向管道中发送信息,在接收到管道信号后,才读取共享内存的内容,这样就可以让共享内存也存在向管道一样的同步机制,写端写一条,读端读一条)

代码完整使用

shm.hpp

#include <iostream>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>#define Creater 1
#define User 2
const char *pathname = "/home/zyq/mydir/dir/Shm";
int proj_id = 0x666;class Shm
{
private:key_t GetComkey(){key_t key = ftok(_pathname, proj_id);if (key < 0){perror("ftok");return -1;}return key;}int GetShmid(key_t key, int size, int flag){int shmid = shmget(_key, size, flag);if (shmid < 0)perror("shmget");return shmid;}void AttachShm(){_addrshm = shmat(_shmid, nullptr, 0);if (_addrshm == nullptr){perror("shmat");}}void DetachShm(){if (_addrshm != nullptr){int n = shmdt(_addrshm);if (n < 0)perror("shmdt");}}public:Shm(const char *pathname, int proj_id, int who): _pathname(pathname), _proj_id(proj_id), _who(who), _addrshm(nullptr){_key = GetComkey();if (who == Creater){GetCreatershmid();}else{GetUsershmid();}//将共享内存连接到进程地址空间AttachShm();std::cout << "_key:" << _key << std::endl;std::cout << "_shmid:" << _shmid << std::endl;}~Shm(){//删除进程地址空间shmctl(_shmid,IPC_RMID,nullptr);//将共享内存脱离进程地址空间DetachShm();}bool GetCreatershmid(){_shmid = GetShmid(_key, 4096, IPC_CREAT | IPC_EXCL|0666 );if (_shmid < 0)return false;else{std::cout << "Create shm done!" << std::endl;return true;}}bool GetUsershmid(){_shmid = GetShmid(_key, 4096, IPC_CREAT|0666);if (_shmid < 0)return false;else{std::cout << "Get shm done!" << std::endl;return true;}}void* Addr(){return _addrshm;}
private:key_t _key;int _shmid;const char *_pathname;int _proj_id;int _who;void *_addrshm;
};

server.cc

#include "shm.hpp"
#include "namedpipe.hpp"
int main()
{// 创建共享内存并连接Shm shm(pathname, proj_id, Creater);char *shmaddr = (char *)shm.Addr();// 创建管道NamedPipe fifo(path, Creater);fifo.OpenforRead();while (true){//读共享内存前先获取唤醒信号std::string str;fifo.ReadNamedPipe(&str);std::cout << "shm content:" << shmaddr << std::endl;sleep(1);}sleep(10);return 0;
}

client.cc

#include"shm.hpp"
#include"namedpipe.hpp"
int main()
{//获取共享内并连接Shm shm(pathname,proj_id,User);char* shmaddr=(char*)shm.Addr();//获取管道NamedPipe fifo(path,User);fifo.OpenforWrite();char ch='A';while(ch<'Z'){shmaddr[ch-'A']=ch;//写完以后,向管道发送唤醒信息//向管道中写的内容不重要,主要是利用管道的同步机制std::cout<<"add "<<ch<<" into shm"<<std::endl;std::string str="WakeupRead";fifo.WriteNamedPipe(str);ch++;sleep(2);}sleep(10);return 0;
}

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

<2024年5月软考高项极限冲刺>《2 考试知识块》

&#x1fab8;&#x1fab8;把你所学串起来&#xff0c;欢迎订阅。&#x1fab8;&#x1fab8; 每章附独家脑图&#xff0c;原图。 冲刺 冲刺 冲刺 1 看下面的图&#xff0c;让你知道你要学习的全部知识是什么 2 章节解析 我们考试的重点是项目管理知识&#xff0c;但是因…

Python零基础-上【详细】

目录 一、Python简介 1、Python发展史 2、Python理解 3、Python的优缺点 &#xff08;1&#xff09;优点 &#xff08;2&#xff09;缺点 二、Python开发环境搭建 1、环境搭建 2、尝试写一个基础程序 &#xff08;1&#xff09;调整配置 &#xff08;2&#xff09;新…

sunshine+n2n+moonlight串流远程控制全教程

远程主机说明&#xff08;两台电脑不在同一局域网下&#xff09;&#xff1a; 控制台电脑 被控制电脑 所有工具下载地址&#xff1a;https://www.lanzouw.com/b00eepod7e 密码:1234 一、首先NTN组网 使用NTN技术创建虚拟局域网&#xff0c;实现设备之间的P2P连接。 NTN组网…

【IO操作】标准IO和文件IO

一.标准IO和文件IO的区别 &#xff08;1&#xff09;一般标准IO指的是C语言的IO操作&#xff0c;文件IO一般指的是Linux系统调用的IO操作。标准O因为是C语言提供的标准库&#xff0c;所以可以在其他操作系统平台编译后可以执行&#xff0c;但是文件IO只能在Linux下使用&#x…

设计编程网站集:真空成型工艺+真空成型机DIY

真空成型工艺 真空成型工艺是一种广泛应用于制造领域的工艺&#xff0c;主要用于制造复杂形状的塑料制品。真空成型工艺具有成本低、生产效率高、制品质量稳定等优点&#xff0c;因此在汽车、电子、医疗器械等行业得到广泛应用&#xff08;还可以用来制作面具&#xff0c;哈哈哈…

满上! —— 十年之约#22(ROI 48%)

原创 | 刘教链 空头在忍耐了很久之后&#xff0c;趁五一劳动节东方放假发动突袭&#xff0c;把BTC&#xff08;比特币&#xff09;打到6万刀以下。这使得我们终于终结了7个月七连涨的趋势&#xff0c;确定4月以收跌结束。 4月开盘70k&#xff0c;最高72.8k&#xff0c;最低59.6…

[随记]Mac安装Docker及运行开源Penpot

下载Docker Desktop for Mac&#xff1a;https://www.docker.com/products/docker-desktop/ 安装Docker Desktop for Mac&#xff0c;安装完成后&#xff0c;启动Docker&#xff0c;然后在终端输入&#xff1a; docker version 在Mac电脑的Desktop&#xff0c;随便创建一个文…

springBootAdmin监控

简介 用于对 Spring Boot 应用的管理和监控。可以用来监控服务是否健康、是否在线、以及一些jvm数据等等 Spring Boot Admin 分为服务端(spring-boot-admin-server)和客户端(spring-boot-admin-client)&#xff0c;服务端和客户端之间采用 http 通讯方式实现数据交互&#xf…

Java中使用Redis实现分布式锁的三种方式

1. 导语 随着软件开发领域的不断演进,并发性已经成为一个至关重要的方面,特别是在资源跨多个进程共享的分布式系统中。 在Java中,管理并发性对于确保数据一致性和防止竞态条件至关重要。 Redis作为一个强大的内存数据存储,为在Java应用程序中实现分布式锁提供了一种高效的…

LeetCode-网络延迟时间(Dijkstra算法)

每日一题 今天刷到一道有关的图的题&#xff0c;需要求单源最短路径&#xff0c;因此使用Dijkstra算法。 题目要求 有 n 个网络节点&#xff0c;标记为 1 到 n。 给你一个列表 times&#xff0c;表示信号经过 有向 边的传递时间。 times[i] (ui, vi, wi)&#xff0c;其中 …

Noisy:一款功能强大的DNS和HTTPS网络流量噪声生成工具

关于Noisy Noisy是一款功能强大的DNS和HTTP/S网络流量噪音生成工具&#xff0c;该工具基于Python开发&#xff0c;可以帮助广大研究人员在进行常规网络浏览时&#xff0c;在后台生成随机的HTTP/DNS网络流量噪声&#xff0c;并以此来提升网络通信数据的安全性和隐蔽性。 支持的…

WSL2连接Windows主机的Mysql

文章目录 需求查看主机IP防火墙设置Mysql设置允许远程连接WSL2连接Mysql 需求 在WSL2&#xff08;本机Ubuntu20.04&#xff09;运行的程序需要将数据写入到本机的Mysql服务器中 查看主机IP 两种办法&#xff1a; Windows主机输入 ipconfig&#xff0c;找到带有WSL后缀的部分…

《十二》Qt各种对话框之FileDialog文件对话框及QMessageBox 消息对话框

QFileDialog 对话框 选择打开一个文件 若要打开一个文件&#xff0c;可调用静态函数 QFileDialog::getOpenFileName()&#xff0c;“打开一个文件”按钮的响应代码如下&#xff1a; void Dialog::on_btnOpen_clicked() { //选择单个文件QString curPathQDir::currentPath()…

如何利用ChatGPT撰写满分文案:技巧与实例解析

在当今社会&#xff0c;随着企业越来越重视宣传推广&#xff0c;文案写作已成为关键的营销手段之一。同时&#xff0c;人工智能的快速发展为文案创作提供了新的工具和方法。例如&#xff0c;ChatGPT这种基于自然语言处理的模型&#xff0c;在协助撰写多种文案方面展现出了极大的…

【智能算法】冠豪猪优化算法(CPO)原理及实现

1.背景 2024年&#xff0c;M Abdel-Basset等人受到冠豪猪防御行为启发&#xff0c;提出了冠豪猪优化算法&#xff08;Crested Porcupine Optimizer, CPO&#xff09;。 2.算法原理 2.1算法思想 CPO使用四种不同的保护机制:视觉、声音、气味和物理攻击。第一和第二防御策略(视…

Linux实训-用户和组的管理

实训1&#xff1a;用户的管理 创建一个新用户user1&#xff0c;设置其主目录为/home/user1。查看/etc/passwd文件的最后一行&#xff0c;看看是如何记录的。查看文件/etc/shadow文件的最后一行&#xff0c;看看如何记录的。给用户user1设置密码。再次查看文件/etc/shadow文件的…

暗区突围端游海外版|暗区突围怎么玩 新手游玩攻略分享

游戏中健康系统与其它射击游戏有很大区别&#xff0c;根据受伤部位、伤势的不同&#xff0c;会有不同的表现。除了头部之外&#xff0c;其它部位如果损坏后继续受到伤害&#xff0c;那么伤害将会分摊到身体其它部位。在暗区内或者暗区外都可以对角色进行治疗&#xff0c;角色不…

【算法入门教育赛1D】环形密码 - 字符串 | C++题解与代码

题目链接&#xff1a;https://www.starrycoding.com/problem/161 题目描述 小 e e e有一个宝箱&#xff0c;这个宝箱有一个长度为 n n n的密码&#xff0c;但是这个密码校验器是一个环形&#xff0c;意思是只要密码从任意一位开始读&#xff08;读到最后一位回到第一位继续&a…

【Canvas与艺术】自制朝阳电脑桌面(1920*1080)

【关键点】 线性渐变色绘制天空&#xff0c;径向渐变色绘制太阳&#xff0c;半透明色制作光芒。 【成图】 【代码】 <!DOCTYPE html> <html lang"utf-8"> <meta http-equiv"Content-Type" content"text/html; charsetutf-8"/&g…

Docker 安装部署 postgres

Docker 安装部署 postgres 1、拉取 postgres 镜像文件 [rootiZbp19a67kznq0h0rgosuxZ ~]# docker pull postgres:latest latest: Pulling from library/postgres b0a0cf830b12: Pull complete dda3d8fbd5ed: Pull complete 283a477db7bb: Pull complete 91d2729fa4d5: Pul…