『Linux从入门到精通』第 ㉕ 期 - System V 共享内存

在这里插入图片描述

文章目录

  • 💐专栏导读
  • 💐文章导读
  • 🐧共享内存原理
  • 🐧共享内存相关函数
    • 🐦key 与 shmid 区别
  • 🐧代码实例

💐专栏导读

🌸作者简介:花想云 ,在读本科生一枚,C/C++领域新星创作者,新星计划导师,阿里云专家博主,CSDN内容合伙人…致力于 C/C++、Linux 学习。

🌸专栏简介:本文收录于 Linux从入门到精通,本专栏主要内容为本专栏主要内容为Linux的系统性学习,专为小白打造的文章专栏。

🌸相关专栏推荐:C语言初阶系列C语言进阶系列C++系列数据结构与算法

💐文章导读

共享内存是一种进程间通信的机制,允许多个进程访问同一块物理内存,以实现数据的共享。通过共享内存,进程可以直接读写共享的内存区域,而无需通过中间的数据传输机制(例如管道或消息队列)进行通信,因此共享内存是最快的IPC形式。

共享内存示意图
在这里插入图片描述

🐧共享内存原理

  1. 创建共享内存: 在一个进程中调用系统调用(例如 shmget),请求创建一块共享内存。这个调用需要指定内存的大小以及一些标志,以控制共享内存的权限和行为。

  2. 关联共享内存: 其他进程通过调用系统调用(例如 shmat)将共享内存附加到它们的地址空间中。这个调用返回指向共享内存区域的指针,使得进程可以直接读写这块内存。

  3. 读写共享内存: 一旦多个进程都关联了同一块共享内存,它们就可以直接对这块内存进行读写操作,就像操作普通的内存一样。因为它们共享同一块物理内存,一个进程对共享内存的修改会立即反映到其他进程的视图中。

  4. 分离共享内存: 当进程不再需要访问共享内存时,它可以调用系统调用(例如 shmdt)将共享内存从它的地址空间中分离。这并不会导致共享内存的删除,只是使得该进程无法再访问这块内存。

  5. 删除共享内存: 当不再需要使用共享内存时,一个进程可以调用系统调用(例如 shmctl)请求删除共享内存。这会导致释放共享内存所占用的系统资源。

特点和注意事项:

  • 共享内存提供了高效的进程间通信方式,因为数据直接存储在物理内存中,无需复制或转移。
  • 进程需要谨慎地协调对共享内存的访问,以避免数据一致性问题。例如,可以使用互斥锁等同步机制。
  • 共享内存的使用需要确保不同进程使用相同的数据结构和协议,以便正确地进行数据交换和共享。
  • 在使用共享内存时,应注意防范竞态条件和死锁等并发编程的问题。

🐧共享内存相关函数

  1. 创建共享内存区域: 使用 shmget 函数创建一个共享内存区域。该函数的原型为:

    #include <sys/ipc.h>
    #include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);
    
    • key 是一个用于标识共享内存的键值。
    • size 是要分配的共享内存的大小(字节数)。
    • shmflg 是一组标志,通常使用 IPC_CREAT 表示如果内存不存在则创建。
  2. 连接到共享内存区域: 使用 shmat 函数将进程连接到已经存在的共享内存区域。该函数的原型为:

    #include <sys/types.h>
    #include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);
    
    • shmid 是共享内存区域的标识符,由 shmget 返回。
    • shmaddr 通常设置为 NULL,让系统自动选择合适的地址。
    • shmflg 可以为 0。
  3. 使用共享内存: 一旦连接到共享内存,进程就可以直接在这块内存中读写数据。

  4. 分离共享内存: 使用 shmdt 函数将进程与共享内存脱离。该函数的原型为:

    #include <sys/types.h>
    #include <sys/shm.h>int shmdt(const void *shmaddr);
    
    • shmaddr 是连接到共享内存区域的地址。
  5. 删除共享内存区域: 使用 shmctl 函数可以删除或控制共享内存区域的属性。如果不再需要共享内存,可以使用 shmctl 函数的 IPC_RMID 命令删除它。函数原型为:

    #include <sys/ipc.h>
    #include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);
    
    • shmid 是共享内存区域的标识符。
    • cmd 为控制命令,可以使用 IPC_RMID 表示删除共享内存。

这些函数提供了对共享内存的创建、连接、使用和删除的基本操作。

🐦key 与 shmid 区别

在上面所示的接口中,shmget 函数需要一个 key_t 类型的参数 key。而其他的函数多数用到 shmid 而不会用到 key,那么这两个参数分别是什么,有什么区别呢?

  1. key(键值):

    • key 是一个整数,用于在共享内存创建过程中唯一标识一个共享内存段。
    • 它并不是由系统自动生成的,而是由应用程序提供的,通常以某种方式与程序的逻辑相关。
    • 可以使用 ftok 函数将路径名和一个整数标识符转换为 key,以便在创建共享内存时使用。
    • key 通常用于在不同的进程之间共享相同的内存块,因此它是创建共享内存的关键参数之一。
  2. shmid(共享内存标识符):

    • shmid 是一个由系统生成的标识符,用于标识已经创建的共享内存段。
    • 在使用 shmget 函数创建共享内存时,它通过返回值返回给调用者,用于后续的操作。
    • shmid 是由系统内核分配的,通常是一个唯一的整数。
    • 通过 shmat 函数将进程连接到共享内存时,需要使用 shmid 作为参数。

总的来说,key 是在共享内存创建时由应用程序指定的用户定义的标识符,而 shmid 是由系统内核在共享内存创建时自动生成的系统级标识符。key 用于唯一标识共享内存的名字,而 shmid 用于在程序运行时标识特定的共享内存实例。

shm可以用于多个进程之间通信,在同一时刻,可能有多个共享内存被用来进行通信。所以系统中一定会有很多个共享内存同时存在,那么系统就会采取一定措施来管理这些共享内存。所以共享内存并不是单单的一块内存空间,系统会为它构建一个结构体对象来描述它。

  • 所以,共享内存 = 内核数据结构 + 真正开辟的内存空间;

共享内存数据结构

struct shmid_ds {
struct ipc_perm shm_perm; /* operation perms */
int shm_segsz; /* size of segment (bytes) */
__kernel_time_t shm_atime; /* last attach time */
__kernel_time_t shm_dtime; /* last detach time */
__kernel_time_t shm_ctime; /* last change time */
__kernel_ipc_pid_t shm_cpid; /* pid of creator */
__kernel_ipc_pid_t shm_lpid; /* pid of last operator */
unsigned short shm_nattch; /* no. of current attaches */
unsigned short shm_unused; /* compatibility */
void *shm_unused2; /* ditto - used by DIPC */
void *shm_unused3; /* unused */
};

两个进程使用共享内存进行通信的前提是,如何让两个进程使用同一块共享内存。内存中有许许多多的共享内存,我们如何让两个进程使用一个共享内存呢?

这就要提到另一个函数 ftok 了。

ftok 函数是一个用于生成System V IPC(Inter-Process Communication,进程间通信)的键值的函数。它的主要用途是在创建System V IPC对象(如消息队列、信号量、共享内存)时,为这些对象生成唯一的键值。

函数原型如下:

#include <sys/types.h>
#include <sys/ipc.h>key_t ftok(const char *pathname, int proj_id);
  • pathname 是一个与文件相关的路径名,用于生成键值。通常是指向一个存在的文件的路径。
  • proj_id 是一个用户定义的整数,用于区分不同的IPC对象。在不同的IPC对象中,如果 pathname 相同,而 proj_id 不同,生成的键值也会不同。

ftok 函数通过将 pathname 转换为一个唯一的键值,以确保在不同的进程中使用相同的 pathnameproj_id 参数生成的键值是一致的。

一般来说,ftok 函数的使用场景是在创建System V IPC对象之前,通过调用 ftok 来生成一个唯一的键值。这个键值将被传递给诸如 msggetsemgetshmget 等函数,用于创建具体的消息队列、信号量或共享内存段。

需要注意的是,ftok 存在一些限制和注意事项,比如需要确保 pathname 指向的文件是存在的,否则 ftok 会返回 -1。此外,由于 proj_id 是一个整数,因此其范围应在0到255之间,以保证生成的键值在合理的范围内。

所以,key 是在内核中使用的,类比文件的 inode 编号。而 shmid 是给用户使用的,类比文件的文件描述符 fd。

🐧代码实例

接下来,我们就通过一个简单的代码设计来熟悉共享内存的使用。该设计的内容是,通过共享内存让两个进程(Server 与 Client)进行通信。

communicate.hpp

该头文件内提供shm所用到的函数方法。

/* communicate.hpp */#ifndef __COMM_HPP__
#define __COMM_HPP__#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <sys/ipc.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/shm.h>
#include <unistd.h>using namespace std;#define PATHNAME "." // 文件路径(用于key生成)
#define PROJID 0x12138 // 项目ID(用于key生成)const int gsize = 4096;key_t getKey() // 获取键值
{key_t key = ftok(PATHNAME, PROJID);if (key == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(1);}return key;
}static int createShmHelper(key_t key, int size, int flag)
{int shmid = shmget(key, gsize, flag);if (shmid == -1){cerr << "error: " << errno << " : " << strerror(errno) << endl;exit(2);}return shmid;
}int createShm(key_t key, int size) // 创建共享内存
{umask(0);return createShmHelper(key, size, IPC_CREAT | IPC_EXCL | 0666);
}int getShm(key_t key, int size) // 获取共享内存
{umask(0);return createShmHelper(key, size, IPC_CREAT);
}char *attachShm(int shmid) // 关联共享内存
{char *start = (char *)shmat(shmid, nullptr, 0); // 将共享内存段连接到进程地址空间return start;
}void detachShm(char *start) // 去关联
{int n = shmdt(start); // 将共享内存段与当前进程脱离assert(n != -1);(void)n;
}void delShm(int shmid) // 释放共享内存
{int n = shmctl(shmid, IPC_RMID, nullptr); // 释放共享内存assert(n != -1);(void)n;
}#define SERVER 1
#define CLIENT 0class Init
{
public:Init(int type): type(type){key_t key = getKey();if (type == SERVER)shmid = createShm(key, gsize);elseshmid = getShm(key, gsize);start = attachShm(shmid);}char *getStart() { return start; }~Init(){detachShm(start);if (type == SERVER)delShm(shmid); // 只有共享内存创建者才负责释放}private:char *start; // 起始地址int type;    // server or clientint shmid;
};#endif

Server

/* Server.cc */
#include "communicate.hpp"
#include <unistd.h>using namespace std;int main()
{   // 建立连接Init init(SERVER);char *start = init.getStart();// 开始通信int n = 0;while (n <= 30){cout << "client -> server# " << start << endl;sleep(1);n++;}return 0;
}

Client

/* Client.cc */
#include "communicate.hpp"
using namespace std;int main()
{// 建立连接Init init(CLIENT);char *start = init.getStart();// 开始通信char c = 'A';while (c <= 'Z'){start[c - 'A'] = c;c++;start[c - 'A'] = 0;sleep(1);}return 0;
}

效果展示

在这里插入图片描述
注意

当我们运行完一次程序后,再次运行程序会发生错误:

$ ./server 
error: 17 : File exists

原因是,上次程序运行时创建的共享内存仍然存在,可以使用 ipcs -m 指令来查看已经有的共享内存:

$ ipcs -m------ Shared Memory Segments --------
key        shmid      owner      perms      bytes      nattch     status      
0x3801203f 0          hxy        666        4096       0                       

由此可见,共享内存的生命周期是随系统的,不随进程。

我们可以使用 ipcrm -m 指令删除指定的共享内存:

$ ipcrm -m shmid

本章的内容到这里就结束了!如果觉得对你有所帮助的话,欢迎三连~

在这里插入图片描述

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

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

相关文章

YOLOv9独家原创改进|加入幽灵卷积Ghost Convolution模块,轻量化!

专栏介绍&#xff1a;YOLOv9改进系列 | 包含深度学习最新创新&#xff0c;主力高效涨点&#xff01;&#xff01;&#xff01; 一、论文摘要 由于内存和计算资源有限&#xff0c;在嵌入式设备上部署卷积神经网络是困难的。特征图中的冗余是那些成功的细胞神经网络的一个重要特征…

【网站项目】158企业人事管理系统

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

在Linux以命令行方式(静默方式/非图形化方式)安装MATLAB(正版)

1.根据教程&#xff0c;下载windows版本matlab&#xff0c;打开图形化界面&#xff0c;选择linux版本的只下载不安装 2.获取安装文件夹 3.获取许可证 4.安装 &#xff08;1&#xff09;跳过引用文章的2.2章节 &#xff08;2&#xff09;本文的安装文件夹代替引用文章的解压IS…

Java进阶(锁)——锁的升级,synchronized与lock锁区别

目录 引出Java中锁升级synchronized与lock锁区别 缓存三兄弟&#xff1a;缓存击穿、穿透、雪崩缓存击穿缓存穿透缓存雪崩 总结 引出 Java进阶&#xff08;锁&#xff09;——锁的升级&#xff0c;synchronized与lock锁区别 Java中锁升级 看一段代码&#xff1a; public class…

Fastwhisper + Pyannote 实现 ASR + 说话者识别

文章目录 前言一、faster-whisper简单介绍二、pyannote.audio介绍三、faster-whisper pyannote.audio 实现语者识别四、多说几句 前言 最近在研究ASR相关的业务&#xff0c;也是调研了不少模型&#xff0c;踩了不少坑&#xff0c;ASR这块&#xff0c;目前中文普通话效果最好的…

Scrapy与分布式开发(1.1):课程导学

Scrapy与分布式开发&#xff1a;从入门到精通&#xff0c;打造高效爬虫系统 课程大纲 在这个专栏中&#xff0c;我们将一起探索Scrapy框架的魅力&#xff0c;以及如何通过Scrapy-Redis实现分布式爬虫的开发。在本课程导学中&#xff0c;我们将为您简要介绍课程的学习目标、内容…

Verilog Coding Styles For Improved Simulation Efficiency论文学习记录

原文基于Verilog-XL仿真器&#xff0c;测试了以下几种方式对仿真效率的影响。 1. 使用 Case 语句而不是 if / else if 语句 八选一多路选择器 case 实现效率比 if / else if 提升 6% 。 2. 如果可以尽量不使用 begin end 语句 使用 begin end 的 ff 触发器比不使用 begin end …

深度相机xyz点云文件三维坐标和jpg图像文件二维坐标的相互变换函数

深度相机同时拍摄xyz点云文件和jpg图像文件。xyz文件里面包含三维坐标[x,y,z]和jpg图像文件包含二维坐标[x&#xff0c;y],但是不能直接进行变换&#xff0c;需要一定的步骤来推演。 下面函数是通过box二维框[xmin, ymin, xmax, ymax, _, _ ]去截取xyz文件中对应box里面的点云…

MyCAT学习——在openEuler22.03中安装MyCAT2(网盘下载版)

准备工作 因为MyCAT 2基于JDK 1.8开发。也需要在虚拟机中安装JDK&#xff08;JDK官网就能下载&#xff0c;我这提供一个捷径&#xff09; jdk-8u401-linux-x64.rpmhttps://pan.baidu.com/s/1ywcDsxYOmfZONpmH9oDjfw?pwdrhel下载对应的tar安装包,以及对应的jar包 安装程序包…

九州金榜|孩子厌学要怎么办?

孩子从小学到初中再到高中&#xff0c;孩子出现厌学情绪很正常&#xff0c;但是孩子出现厌学情绪后&#xff0c;就必然会影响到孩子学习成绩&#xff0c;孩子产生厌学情绪的原因有哪些呢&#xff1f;只有找准孩子厌学原因才能去帮助孩子怎样去克服孩子厌学情绪&#xff0c;下面…

day34贪心算法 part03

1005. K 次取反后最大化的数组和 简单 给你一个整数数组 nums 和一个整数 k &#xff0c;按以下方法修改该数组&#xff1a; 选择某个下标 i 并将 nums[i] 替换为 -nums[i] 。 重复这个过程恰好 k 次。可以多次选择同一个下标 i 。 以这种方式修改数组后&#xff0c;返回数…

OSError: [WinError 1455] 页面文件太小,无法完成操作。

[问题描述]&#xff1a;OSError: [WinError 1455] 页面文件太小&#xff0c;无法完成操作。 原因1&#xff1a;线程数太大 方法&#xff1a;改小线程&#xff08;workers&#xff09;数。 原因2&#xff1a;虚拟内存太小或为0&#xff0c;调大虚拟内存。 方法&#xff1a;右键…

Vue.js 实用技巧:深入理解 Vue.mixin

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

【Linux进程间通信】共享内存

【Linux进程间通信】共享内存 目录 【Linux进程间通信】共享内存system V共享内存共享内存示意图共享内存的数据结构共享内存函数将共享内存挂接到对应的进程将共享内存取消挂接释放共享内存 共享内存的特性共享内存扩展共享内存配合管道进行使用 作者&#xff1a;爱写代码的刚…

用docker部署后端项目

一、搭建局域网 1.1、介绍前后端项目搭建 需要4台服务器&#xff0c;在同一个局域网中 1.2、操作 # 搭建net-ry局域网&#xff0c;用于部署若依项目 net-ry&#xff1a;名字 docker network create net-ry --subnet172.68.0.0/16 --gateway172.68.0.1#查看 docker network ls…

3d模型合并后一片漆黑是什么原因,怎么解决---模大狮模型网

当合并多个3D模型后&#xff0c;发现整个合并后的模型显示为一片漆黑通常是由以下几个可能的原因导致的&#xff1a; 材质设置问题&#xff1a;合并后的模型可能存在材质设置错误&#xff0c;导致模型无法正确显示。检查每个模型的材质属性&#xff0c;确保其正确设置&#xff…

老隋蓝海项目有哪些?能赚钱吗?

在创业的海洋中&#xff0c;每个人都渴望找到那片属于自己的“蓝海”&#xff0c;而“老隋蓝海项目”便是许多人心中的那片未知海域。那么&#xff0c;老隋蓝海项目究竟是指什么?它们又能否成为创业者的新财富之源? 蓝海项目的定义 我们要明白&#xff0c;蓝海项目通常指的是…

【漏洞复现】某厂商明御WEB应用防火墙任意用户登录漏洞

Nx01 产品简介 安恒明御WEB应用防火墙&#xff08;简称WAF&#xff09;是杭州安恒信息技术股份有限公司自主研发的一款专业应用安全防护产品&#xff0c;专注于为网站、APP等Web业务系统提供安全防护。 Nx02 漏洞描述 安恒明御WEB应用防火墙report.php文件存在硬编码设置的Con…

yolov7添加spd-conv注意力机制

一、spd-conv是什么&#xff1f; SPD-Conv&#xff08;Symmetric Positive Definite Convolution&#xff09;是一种新颖的卷积操作&#xff0c;它主要应用于处理对称正定矩阵&#xff08;SPD&#xff09;数据。在传统的卷积神经网络&#xff08;CNN&#xff09;中&#xff0c;…

人工智能_大模型013_AIGC生成式模型的增强检索_RAG知识补充检索_补充私域和实时场景知识_关键字检索增强---人工智能工作笔记0149

什么是RAG,RAG的意思就是,如果一套生成式AIGC大模型,你昨天训练了以后,那么今天的知识,还没有给他进行训练,那么回答的时候,他就会遗漏今天的知识,那么我们就可以通过检索的手段,把今天的知识,检索出来,然后补充道prompt中,给这个大模型.让他参考,这样就包含了今天的知识相当于…