【Linux】—— 共享内存

简介: 【Linux】—— 共享内存



(一)深刻理解共享内存

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

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

相关文章
|
2月前
|
安全 Linux Shell
Linux上执行内存中的脚本和程序
【9月更文挑战第3天】在 Linux 系统中,可以通过多种方式执行内存中的脚本和程序:一是使用 `eval` 命令直接执行内存中的脚本内容;二是利用管道将脚本内容传递给 `bash` 解释器执行;三是将编译好的程序复制到 `/dev/shm` 并执行。这些方法虽便捷,但也需谨慎操作以避免安全风险。
176 6
|
10天前
|
算法 Linux 开发者
深入探究Linux内核中的内存管理机制
本文旨在对Linux操作系统的内存管理机制进行深入分析,探讨其如何通过高效的内存分配和回收策略来优化系统性能。文章将详细介绍Linux内核中内存管理的关键技术点,包括物理内存与虚拟内存的映射、页面置换算法、以及内存碎片的处理方法等。通过对这些技术点的解析,本文旨在为读者提供一个清晰的Linux内存管理框架,帮助理解其在现代计算环境中的重要性和应用。
|
16天前
|
存储 缓存 监控
|
1月前
|
存储 缓存 监控
Linux中内存和性能问题
【10月更文挑战第5天】
38 4
|
1月前
|
算法 Linux
Linux中内存问题
【10月更文挑战第6天】
41 2
|
13天前
|
缓存 算法 Linux
Linux内核中的内存管理机制深度剖析####
【10月更文挑战第28天】 本文深入探讨了Linux操作系统的心脏——内核,聚焦其内存管理机制的奥秘。不同于传统摘要的概述方式,本文将以一次虚拟的内存分配请求为引子,逐步揭开Linux如何高效、安全地管理着从微小嵌入式设备到庞大数据中心数以千计程序的内存需求。通过这段旅程,读者将直观感受到Linux内存管理的精妙设计与强大能力,以及它是如何在复杂多变的环境中保持系统稳定与性能优化的。 ####
22 0
|
1月前
|
存储 缓存 固态存储
|
1月前
|
Linux C++
Linux c/c++文件虚拟内存映射
这篇文章介绍了在Linux环境下,如何使用虚拟内存映射技术来提高文件读写的速度,并通过C/C++代码示例展示了文件映射的整个流程。
46 0
|
3月前
|
Linux 调度
深入理解Linux虚拟内存管理(七)(下)
深入理解Linux虚拟内存管理(七)
67 4
|
3月前
|
存储 Linux 索引
深入理解Linux虚拟内存管理(九)(中)
深入理解Linux虚拟内存管理(九)
36 2