【Linux】—— 共享内存

本期我将要带大家学习的是有关进程间通信的另一种方式——共享内存。共享内存是一种用于进程间通信的高效机制,允许多个进程访问和操作同一块内存区域。

目录

(一)深刻理解共享内存

1.1 概念解释

1.2 共享内存原理

1.3 共享内存数据结构

1.4 共享内存函数

(二) 代码实现


(一)深刻理解共享内存

1.1 概念解释

  • 共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到内核,它允许不同的进程通过映射同一块物理内存区域来实现数据的直接读写,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据

1.2 共享内存原理
 

  • 首先大家要明白要进入通信,前提是不是得有进程,而且对于共享内存它不像匿名管道那样必须得是父子进程;
  • 对于共享内存任意两个进程之间都可以创建,其中它的通信原理,我们在思考的时候一定要先想明白,只要此时存在一个进程它就有己的PCB、它就要有自己的地址空间、它就有自己的页表;
  • 同样的对于另一个进程,在被创建的时候也要有自己的PCB、也要有自己的地址空间、那么更要有自己的页表结构好。

【解释说明】 

  1. 不管是进程a还是B,这两进程当中任一个进程,它对应的都要将自己的数据,要能够映射到物理内存当中的特定区域;
  2. 最终它的地址空间当中,所有的代码和数据都会经过我们对应的页表映射到我们对应的物理内存当中,进而让我们找到该进程匹配的代码和数据;
  3. 对于进程B来讲它也有自己的地址空间,它也有自己的页表,最终可以映射到我们对应的内存当中的某一些特定的区域来代表它的代码或者数据;那么同样的进程a也有自己的代码和数据,包括它自己的,还有动态库等等,最后也会映射到我们物理内存当中的特定的区域

【解释说明】

  • 第一步:我们先有一段儿我们对应的物理内存的空间;
  • 第二步:对于进程A可以把一个库加载内存映射到他这个地址空间里,然后返回给用户,或者让对应的代码区的代码直接跳转到库里面去跑,而我们如果我创建了一块儿内存,然后想办法把对应的这块儿内存经过页表映射到我们对应的地址空间当中的某一个区域;然后我把这块儿地址空间对应的区域地址返回给用户。我们这里就相当于构建从共享内存到当前进程的地址空间内的映射,然后在这里将对应空间它的起始地址返回给用户;
  • 第三步:同样的进程B,它如果也做同样的工作也将对应的这部分地址空间映射到自己的那么共享区当中,将对应的代码和数据的那个虚拟起始地址,尤其是内存块儿的起始地址返回给用户,那么未来进程A和进程B只要使用对应的起始的虚拟地址就可以访问了。
  • 此时我们不就完成了让不同的进程A和进程B看到了同一份儿资源,这就叫做共享内存!!!

1.3 共享内存数据结构

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 */
};

1.4 共享内存函数

在Linux系统中,共享内存通常通过系统调用来实现。以下是一些与共享内存相关的主要函数:

ftok函数:

  • 功能:生成一个唯一的key,用于标识共享内存段。
  • 参数
    • keyfile:与路径名相关的文件名,可以是一个存在的文件。
    • id:一个非零整数。
  • 返回值:生成的key。

 man 手册查询结果:


shmget函数

功能:用来创建共享内存
原型:int shmget(key_t key, size_t size, int shmflg);
参数

  • key:这个共享内存段名字
  • size:共享内存大小
  • shmflg:由九个权限标志构成,它们的用法和创建文件时使用的mode模式标志是一样的

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

  man 手册查询结果:


shmat函数
功能:将共享内存段连接到进程地址空间
原型:void *shmat(int shmid, const void *shmaddr, int shmflg);
参数

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

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

  man 手册查询结果:

说明:

  •  shmaddr为NULL,核心自动选择一个地址
  • shmaddr不为NULL且shmflg无SHM_RND标记,则以shmaddr为连接地址。
  • shmaddr不为NULL且shmflg设置了SHM_RND标记,则连接的地址会自动向下调整为SHMLBA的整数倍。公式:shmaddr -(shmaddr % SHMLBA)
  • shmflg=SHM_RDONLY,表示连接操作用来只读共享内存

 shmdt函数

功能:将共享内存段与当前进程脱离
原型:int shmdt(const void *shmaddr);
参数

  • shmaddr: 由shmat所返回的指针

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

  man 手册查询结果: 


shmctl函数
功能:用于控制共享内存
原型:int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数

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

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

   man 手册查询结果: 


(二) 代码实现

接下来,我们就用上述描述的接口简单的实现一下共享内存。 

【client.cc】

#include"common.hpp"
#include<unistd.h>
int main()
{
    key_t k = getkey();
    cout << "client key: " << toHex(k) << endl;

    int shmid = getShm(k, gsize);
    cout << "client shmid: " <<shmid<< endl;

    //3. 将自己和共享内存关联起来
    char* start = attachShm(shmid);

    sleep(15);
    
    // 4.  将自己和共享内存去关联
    detachShm(start);
    return 0;
}

【server.cc】

#include"common.hpp"
#include<unistd.h>
int main()
{
    //1. 创建key
    key_t k = getkey();
    cout << "server key: " << toHex(k) << endl;

    //2. 创建共享内存
    int shmid = createShm(k, gsize);
    cout << "server shmid: " << shmid << endl;

    sleep(3);

    //3. 将自己和共享内存关联起来
    char* start = attachShm(shmid);
    sleep(15);

    // 4. 将自己和共享内存去关联
    detachShm(start);

    //删除共享内存
    delShm(shmid);
    return 0;
}

【common.hpp】

#ifndef __COMMON_HPP__
#define __COMMON_HPP__

#include <iostream>
#include <cerrno>
#include <cstdio>
#include <cstring>
#include <cassert>
#include <string>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <sys/types.h>
#include <sys/stat.h>
using namespace std;

#define PATHNAME "."
#define PROJID 0x6666
const int gsize = 4096; //暂时

key_t getkey(){
    key_t k =ftok(PATHNAME,PROJID);
    if(k == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(1);
    }
    return k;
}

string toHex(int x){
    char buffer[64];
    snprintf(buffer, sizeof buffer, "0x%x", x);
    return buffer;
}

static int createShmHelper(key_t k, int size, int flag){
    int shmid = shmget(k, gsize, flag);
    if(shmid == -1)
    {
        cerr << "error: " << errno << " : " << strerror(errno) << endl;
        exit(2);
    }
    return shmid;
}


int createShm(key_t k, int size){
    umask(0);
    return createShmHelper(k, size, IPC_CREAT | IPC_EXCL | 0666);
}

int getShm(key_t k, int size)
{
    return createShmHelper(k, size, IPC_CREAT);
}

void delShm(int shmid){
    int n = shmctl(shmid,IPC_RMID,nullptr);
    assert(n != -1);
    (void)n;
}

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;
}

#endif

总的来说,共享内存是一种强大的进程间通信机制,适用于需要高效、频繁共享大量数据的场景。在使用时需要注意同步和互斥机制,以确保数据的一致性和安全性。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Spring | Srping AOP (AOP简介、动态代理、基于“代理类”的AOP实现)

目录: 1.Spring AOP简介1.1 AOP简介1.2 AOP术语 2.动态代理2.1 JDK动态代理2.2 CGLIB代理 3.基于“代理类”的AOP实现3.1 Spring的通知类型3.2 ProxyFactoryBean ( 可通知.xml配置文件完成aop功能 ) 1.Spring AOP简介 1.1 AOP简介 Spring的AOP模块&#xff0c;是Spring框架体系…

【C++】list容器迭代器的模拟实现

list容器内部基本都是链表形式实现&#xff0c;这里的迭代器实现的逻辑需要注意C语言中指针的转换。 list容器如同数据结构中的队列&#xff0c;通常用链式结构进行存储。在这个容器中&#xff0c;我们可以模仿系统的逻辑&#xff0c;在头结点后设置一个“ 哨兵 ”&#xff0c;…

什么勒索攻击,应该如何防护?

当前&#xff0c;勒索攻击、僵尸网络攻击、DDos攻击、APT攻击、挖矿攻击、供应链攻击、网站攻击、电信诈骗等各种攻击手段层出不穷。 勒索攻击应该是今年网络安全行业讨论最多的话题&#xff0c;勒索钱财或者窃取商业数据是黑产最主要的目的。 勒索软件的攻击特征 与其它攻击行…

限价单和止损单是什么?澳福实例讲解

什么是限价单和止损单&#xff0c;投资者如何使用它?了解交易基础知识&#xff0c;快速进入市场盈利收场&#xff0c;今天fpmarkets澳福和各位投资者继续探讨交易基础知识。 止损单是在看涨趋势中以更高的价格买入的前提&#xff0c;通过图表得知&#xff0c;黄线显示欧元兑美…

刷题 ------ 排序

文章目录 1.K 次取返后最大化的数组和&#xff08;堆&#xff09;2.数组的相对排序&#xff08;桶&#xff09;3.最小绝对差4.根据数字二进制下1的数目排序&#xff08;qsort&#xff09;5.有多少小于当前数字的数字6.非递增顺序的最小子序列7.按照频率将数组升序排序&#xff…

生成当天递增唯一的流水号的几种方式

说明&#xff1a;当开发中&#xff0c;如交易、文件传输过程中的文件名&#xff0c;可能需要我们使用一串唯一的数字来锁定这一条“交互记录”&#xff0c;即流水号。 本文介绍几种生成6位递增唯一&#xff0c;且每日重置的流水号的方式。 方式一&#xff1a;使用Redis 我们…

Leetcode—22.括号生成【中等】

2023每日刷题&#xff08;七十九&#xff09; Leetcode—22.括号生成 算法思想 实现代码 class Solution { public:vector<string> generateParenthesis(int n) {vector<string> ans;int m n * 2;string path(m, 0);function<void(int, int)> dfs [&…

自己构建webpack+vue3+ts

先看看我的目录结构&#xff08;我全局使用TS&#xff09;&#xff1a; 一、安装配置webpack打包 安装esno npm install esnoesno 是基于 esbuild 的 TS/ESNext node 运行时,有了它&#xff0c;就可以直接通过esno *.ts的方式启动脚本&#xff0c;package.json中添加 type:…

力扣62. 不同路径

动态规划 思路&#xff1a; 定义 dp[r][c] 为到达坐标 (r, c) 的路径数&#xff1a; 它只能有同一行左边相邻方格向右到达或者同一列上方相邻方格向下到达&#xff1b;状态转移方程&#xff1a; dp[r][c] dp[r][c - 1] dp[r - 1][c]初始状态 dp[0][0] 1第一行的路径数是 1第…

二维码地址门牌管理系统:社区新风向

文章目录 前言一、集成先进技术的系统二、便捷居民体验三、支持社区管理四、未来展望与可扩展性 前言 随着科技的不断发展&#xff0c;智能化管理已经深入到我们的生活中。二维码门牌管理系统作为一款创新产品&#xff0c;在社区管理领域迅速引起广泛关注。这款系统不仅提升了…

CTFhub-bak文件

CTFhub-Web-信息泄露-备份文件下载-bak文件 题目信息 解题过程 看到提示说和index.php有关&#xff0c;在url后面加index.php.bak&#xff0c;跳转到http://challenge-7a4da2076cfabae6.sandbox.ctfhub.com:10800/index.php.bak网址&#xff0c;即&#xff1a; 跳转到下载页…

彻底搞定让人头痛的nginx location 路径匹配规则

nginx location 路径匹配规则 一、前言二、说在前面三、开始表演 一、前言 很多同学&#xff0c;在配置nginx的时候&#xff0c;都会遇到一个头痛的问题&#xff0c;就是location 的路径应该怎么写&#xff1f;到底要不要加斜杠&#xff0c;有点傻傻分不清楚。今天就来帮助大家…

蓝桥杯真题(Python)每日练Day3

题目 题目分析 为了找到满足条件的放置方法&#xff0c;可以带入总盘数为2和3的情景&#xff0c;用递归做法实现。 A中存在1 2两个盘&#xff0c;为了实现最少次数放入C且上小下大&#xff0c;先将1放入B&#xff0c;再将2放入C&#xff0c;最后将1放入C即可。同理当A中存在1 …

柔性数组和C语言内存划分

柔性数组和C语言内存划分 1. 柔性数组1.1 柔性数组的特点&#xff1a;1.2 柔性数组的使用1.3 柔性数组的优势 2. 总结C/C中程序内存区域划分 1. 柔性数组 也许你从来没有听说过柔性数组&#xff08;flexible array)这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff…

以太坊账户地址与比特B地址生成方法对比

作者 张群&#xff08;赛联区块链教育首席讲师&#xff0c;工信部赛迪特聘资深专家&#xff0c;CSDN认证业界专家&#xff0c;微软认证专家&#xff0c;多家企业区块链产品顾问&#xff09;关注张群&#xff0c;为您提供一站式区块链技术和方案咨询。 以太坊和比特B地址在生成方…

C语言——内存函数介绍和模拟实现(memcpy、memmove、memset、memcmp)

之前我们讲过一些字符串函数&#xff08;http://t.csdnimg.cn/ZcvCo&#xff09;&#xff0c;今天我们来讲一讲几个内存函数&#xff0c;那么可能有人要问了&#xff0c;都有字符串函数了&#xff0c;怎么又来个内存函数&#xff0c;这不是一样的么&#xff1f; 我们要知道之前…

【C语言】指针进阶之sizeof和strlen函数的对比

目录 1.sizeofyu 2.strlen函数 3.sizeof与strlen的对比 1.sizeof >>sizeof计算变量所占内存内存空间 大小 的&#xff0c;单位是 字节 &#xff0c;如果操作数是类型的话&#xff0c;计算的是使⽤类型创建的变量所占内存空间的大小。 >>sizeof 只关注占⽤内存空…

Spring5系列学习文章分享---第一篇(概述+特点+IOC原理+IOC并操作之bean的XML管理操作)

目录 Spring&#xff08;概述特点IOC原理IOC并操作之bean的XML管理操作&#xff09;概述Spring是轻量级的开源的JavaEE框架Spring可以解决企业应用开发的复杂性Spring有两个核心部分ioc,aopSpring特点 loc(概念和原理)什么是 IOCIOC 底层原理IOC 过程图 IOC&#xff08;接口&am…

Java和Redis实现一个简单的热搜功能

1. 前言 我们有一个简单的需求&#xff1a; 搜索栏展示当前登陆的个人用户的搜索历史记录&#xff0c;删除个人历史记录。用户在搜索栏输入某字符&#xff0c;则将该字符记录下来 以zset格式存储的redis中&#xff0c;记录该字符被搜索的个数以及当前的时间戳 &#xff08;用…

阿里云优惠整理,最新2024阿里云优惠政策解读

阿里云优惠政策有哪些&#xff1f;2024年阿里云优惠政策风向改了&#xff0c;之前一直是老用户与狗的营销策略&#xff0c;今年阿里云2核2G、3M固定带宽服务器99元居然开启了老用户购买权限&#xff0c;并且续费不涨价&#xff0c;阿里云这波操作确实让用户赢麻了&#xff0c;在…