Linux进程间通信——共享内存

Linux进程间通信——共享内存

  • 1、创建/打开共享内存
    • 1.1 shmget
    • 1.2 ftok
  • 2、关联和接触关联
    • 2.1 shmat
    • 2.2 shmdt
  • 3、删除共享内存
    • 3.1 shmctl
  • 3.2 相关shell命令
  • 3.3 共享内存状态
  • 4、进程间通信
  • 5、shm和mmap的区别

原文链接

共享内存不同于内存映射区,它不属于任何进程,并且不受进程生命周期的影响。通过调用Linux提供的系统函数就可得到这块共享内存。使用之前需要让进程和共享内存进行关联,得到共享内存的起始地址之后就可以直接进行读写操作了,进程也可以和这块共享内存解除关联, 解除关联之后就不能操作这块共享内存了。在所有进程间通信的方式中共享内存的效率是最高的。

共享内存操作默认不阻塞,如果多个进程同时读写共享内存,可能出现数据混乱,共享内存需要借助其他机制来保证进程间的数据同步,比如:信号量,共享内存内部没有提供这种机制。

1、创建/打开共享内存

1.1 shmget

在使用共享内存之前必须要先做一些准备工作,如果共享内存不存在就需要先创建出来,如果已经存在了就需要先打开这块共享内存。不管是创建还是打开共享内存使用的函数是同一个,函数原型如下:

#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
  • 参数:
    • key: 类型 key_t 是个整形数, 通过这个key可以创建或者打开一块共享内存,该参数的值一定要大于0
    • size: 创建共享内存的时候, 指定共享内存的大小,如果是打开一块存在的共享内存, size是没有意义的
    • shmflg:创建共享内存的时候指定的属性
      • IPC_CREAT: 创建新的共享内存,如果创建共享内存, 需要指定对共享内存的操作权限,比如:IPC_CREAT | 0664
      • IPC_EXCL: 检测共享内存是否已经存在了,必须和 IPC_CREAT一起使用
  • 返回值:共享内存创建或者打开成功返回标识共享内存的唯一的ID,失败返回-1

场景1:创建一块大小为4k的共享内存

shmget(100, 4096, IPC_CREAT|0664);

场景2:创建一块大小为4k的共享内存, 并且检测是否存在

// 	如果共享内存已经存在, 共享内存创建失败, 返回-1, 可以perror() 打印错误信息
shmget(100, 4096, IPC_CREAT|0664|IPC_EXCL);

场景3:打开一块已经存在的共享内存

// 函数参数虽然指定了大小和IPC_CREAT, 但是都不起作用, 因为共享内存已经存在, 只能打开, 参数4096也没有意义
shmget(100, 4096, IPC_CREAT|0664);
shmget(100, 0, 0);

场景4:打开一块共享内存, 如果不存在就创建

shmget(100, 4096, IPC_CREAT|0664);

1.2 ftok

shmget() 函数的第一个参数是一个大于0的正整数,如果不想自己指定可以通过 ftok()函数直接生成这个key值。该函数的函数原型如下:

// ftok函数原型
#include <sys/types.h>
#include <sys/ipc.h>// 将两个参数作为种子, 生成一个 key_t 类型的数值
key_t ftok(const char *pathname, int proj_id);
  • 参数:

    • pathname: 当前操作系统中一个存在的路径

    • proj_id: 这个参数只用到了int中的一个字节, 传参的时候要将其作为 char 进行操作,取值范围: 1-255

  • 返回值:函数调用成功返回一个可用于创建、打开共享内存的key值,调用失败返回-1

使用举例:

// 根据路径生成一个key_t
key_t key = ftok("/home/robin", 'a');
// 创建或打开共享内存
shmget(key, 4096, IPC_CREATE|0664);

2、关联和接触关联

2.1 shmat

创建/打开共享内存之后还必须和共享内存进行关联,这样才能得到共享内存的起始地址,通过得到的内存地址进行数据的读写操作,关联函数的原型如下:

void *shmat(int shmid, const void *shmaddr, int shmflg);
  • 参数:
    • shmid: 要操作的共享内存的ID, 是 shmget() 函数的返回值
    • shmaddr: 共享内存的起始地址, 用户不知道, 需要让内核指定, 写NULL
    • shmflg: 和共享内存关联的对共享内存的操作权限
      • SHM_RDONLY: 读权限, 只能读共享内存中的数据
      • 0: 读写权限,可以读写共享内存数据
  • 返回值:关联成功,返回值共享内存的起始地址,关联失败返回 (void *) -1

2.2 shmdt

当进程不需要再操作共享内存,可以让进程和共享内存解除关联,另外如果没有执行该操作,进程退出之后,结束的进程和共享内存的关联也就自动解除了。

int shmdt(const void *shmaddr);
  • 参数:shmat() 函数的返回值, 共享内存的起始地址

  • 返回值:关联解除成功返回0,失败返回-1

3、删除共享内存

3.1 shmctl

shmctl() 函数是一个多功能函数,可以设置、获取共享内存的状态也可以将共享内存标记为删除状态。当共享内存被标记为删除状态之后,并不会马上被删除,直到所有的进程全部和共享内存解除关联,共享内存才会被删除。因为通过shmctl()函数只是能够标记删除共享内存,所以在程序中多次调用该操作是没有关系的。

// 共享内存控制函数
int shmctl(int shmid, int cmd, struct shmid_ds *buf);// 参数 struct shmid_ds 结构体原型          
struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */// 引用计数, 多少个进程和共享内存进行了关联shmatt_t        shm_nattch;  /* 记录了有多少个进程和当前共享内存进行了管联 */...
};
  • 参数:
    • shmid: 要操作的共享内存的ID, 是 shmget() 函数的返回值
    • cmd: 要做的操作
      • IPC_STAT: 得到当前共享内存的状态
      • IPC_SET: 设置共享内存的状态
      • IPC_RMID: 标记共享内存要被删除了
    • buf:
      • cmd==IPC_STAT, 作为传出参数, 会得到共享内存的相关属性信息
      • cmd==IPC_SET, 作为传入参, 将用户的自定义属性设置到共享内存中
      • cmd==IPC_RMID, buf就没意义了, 这时候buf指定为NULL即可
  • 返回值:函数调用成功返回值大于等于0,调用失败返回-1

3.2 相关shell命令

使用ipcs 添加参数-m可以查看系统中共享内存的详细信息

$ ipcs -m------------ 共享内存段 --------------
键        shmid      拥有者  权限     字节     nattch     状态      
0x00000000 425984     oracle     600        524288     2          目标       
0x00000000 327681     oracle     600        524288     2          目标       
0x00000000 458754     oracle     600        524288     2          目标 	

使用 ipcrm 命令可以标记删除某块共享内存

# key == shmget的第一个参数
$ ipcrm -M shmkey  # id == shmget的返回值
$ ipcrm -m shmid	

3.3 共享内存状态

// 参数 struct shmid_ds 结构体原型          
struct shmid_ds {struct ipc_perm shm_perm;    /* Ownership and permissions */size_t          shm_segsz;   /* Size of segment (bytes) */time_t          shm_atime;   /* Last attach time */time_t          shm_dtime;   /* Last detach time */time_t          shm_ctime;   /* Last change time */pid_t           shm_cpid;    /* PID of creator */pid_t           shm_lpid;    /* PID of last shmat(2)/shmdt(2) */// 引用计数, 多少个进程和共享内存进行了关联shmatt_t        shm_nattch;  /* 记录了有多少个进程和当前共享内存进行了管联 */...
};

通过shmctl()我们可以得知,共享内存的信息是存储到一个叫做struct shmid_ds的结构体中,其中有一个非常重要的成员叫做shm_nattch,在这个成员变量里边记录着当前共享内存关联的进程的个数,一般将其称之为引用计数。当共享内存被标记为删除状态,并且这个引用计数变为0之后共享内存才会被真正的被删除掉。

当共享内存被标记为删除状态之后,共享内存的状态也会发生变化,共享内存内部维护的key从一个正整数变为0,其属性从公共的变为私有的。这里的私有是指只有已经关联成功的进程才允许继续访问共享内存,不再允许新的进程和这块共享内存进行关联了。下图演示了共享内存的状态变化:

在这里插入图片描述

4、进程间通信

使用共享内存实现进程间通信的操作流程如下:

1. 调用linux的系统API创建一块共享内存- 这块内存不属于任何进程, 默认进程不能对其进行操作2. 准备好进程A, 和进程B, 这两个进程需要和创建的共享内存进行关联- 关联操作: 调用linux的 api- 关联成功之后, 得到了这块共享内存的起始地址3. 在进程A或者进程B中对共享内存进行读写操作- 读内存: printf();- 写内存: memcpy();4. 通信完成, 可以让进程A和B和共享内存解除关联- 解除成功, 进程A和B不能再操作共享内存了- 共享内存不受进程生命周期的影响的5. 共享内存不在使用之后, 将其删除- 调用linux的api函数, 删除之后这块内存被内核回收了

写共享内存的进程代码:

#include <stdio.h>
#include <sys/shm.h>
#include <string.h>int main()
{// 1. 创建共享内存, 大小为4kint shmid = shmget(1000, 4096, IPC_CREAT|0664);if(shmid == -1){perror("shmget error");return -1;}// 2. 当前进程和共享内存关联void* ptr = shmat(shmid, NULL, 0);if(ptr == (void *) -1){perror("shmat error");return -1;}// 3. 写共享内存const char* p = "hello, world, 共享内存真香...";memcpy(ptr, p, strlen(p)+1);// 阻塞程序printf("按任意键继续, 删除共享内存\n");getchar();shmdt(ptr);// 删除共享内存shmctl(shmid, IPC_RMID, NULL);printf("共享内存已经被删除...\n");return 0;
}

读共享内存的进程代码:

#include <stdio.h>
#include <sys/shm.h>
#include <string.h>int main()
{// 1. 创建共享内存, 大小为4k,其实这里是关联!!!int shmid = shmget(1000, 0, 0);if(shmid == -1){perror("shmget error");return -1;}// 2. 当前进程和共享内存关联void* ptr = shmat(shmid, NULL, 0);if(ptr == (void *) -1){perror("shmat error");return -1;}// 3. 读共享内存printf("共享内存数据: %s\n", (char*)ptr);// 阻塞程序printf("按任意键继续, 删除共享内存\n");getchar();shmdt(ptr);// 删除共享内存shmctl(shmid, IPC_RMID, NULL);printf("共享内存已经被删除...\n");return 0;
}

两个进程之间就是依靠key,实现同一片共享内存的关联,这里key = 1000

5、shm和mmap的区别

共享内存内存映射区都可以实现进程间通信,下面来分析一下二者的区别:

  • 实现进程间通信的方式

    • shm: 多个进程只需要一块共享内存就够了,共享内存不属于进程,需要和进程关联才能使用
    • 内存映射区: 位于每个进程的虚拟地址空间中, 并且需要关联同一个磁盘文件才能实现进程间数据通信
  • 效率:

    • shm: 直接对内存操作,效率高
    • 内存映射区: 需要内存和文件之间的数据同步,效率低
  • 生命周期

    • 内存映射区:进程退出, 内存映射区也就没有了
    • shm:进程退出对共享内存没有影响,调用相关函数/命令/ 关机才能删除共享内存
  • 数据的完整性 -> 突发状态下数据能不能被保存下来(比如: 突然断电)

    • 内存映射区:可以完整的保存数据, 内存映射区数据会同步到磁盘文件
    • shm:数据存储在物理内存中, 断电之后系统关闭, 内存数据也就丢失了

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

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

相关文章

基于现代学徒制的大数据技术与应用人才培养模式探讨

学生学徒制的实施旨在解决当前新技术企业招聘技能人才难和青年就业难的结构性矛盾&#xff0c;通过生态链链主企业携手院校共同解决毕业年度学生就业问题&#xff0c;按照学生个人意愿&#xff0c;建立以就业导向的学生学徒制关系&#xff0c;签订学徒培养协议确定学生就业岗位…

【Java基础】几种拼接字符串的方法

几种拼接字符串的方法 1.使用 "" 运算符拼接字符串2.使用 StringBuilder 或 StringBuffer 类3.使用 StringJoiner 类4.使用 String 类 join 方法5.使用 StringUtils 类6.使用 String 类 concat 方法7.使用 String.format() 方法格式化字符串8.使用 Stream 实现9.总结…

Python-图片去重

直接上代码 # 修改一下第34行文件夹路径以及13行图片后缀名即可使用 import os from hashlib import md5def remove_duplicate_images(folder_path):image_files []duplicate_images set()# 遍历文件夹&#xff0c;找到所有 JPG 图片文件for root, dirs, files in os.walk(f…

智能优化算法应用:基于黑猩猩算法无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于黑猩猩算法无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于黑猩猩算法无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.黑猩猩算法4.实验参数设定5.算法结果6.参考文献7.…

Proteus8.16仿真软件安装图文教程(Proteus 8 Professional)

Proteus8.16 &#x1f527;软件安装包下载链接&#xff1a;&#x1f527;视频教程&#x1f527;1 安装软件解压&#x1f527;2 安装&#x1f527;3 破解&#x1f527;4 汉化 &#x1f527;软件安装包下载链接&#xff1a; Proteus8.16软件下载链接 1、本文关于Proteus8.16 SP…

双击热备方案实现(全)

双击热备是应用与服务器的一种解决方案&#xff0c;其构造思想是主机和从机通过TCP/IP网络连接&#xff0c;正常情况下主机处于工作状态&#xff0c;从机处于监视状态&#xff0c;一旦从机发现主机异常&#xff0c;从机将会在很短的时间内代替主机。完全实现主机的功能。 要想实…

golang之net/http模块学习

文章目录 开启服务开启访问静态文件获取现在时间按时间创建一个空的json文件按时间创建一个固定值的json文件 跨域请求处理输出是json 开启服务 package mainimport ("fmt""net/http" )//路由 func handler(w http.ResponseWriter, r *http.Request){fmt.…

2023年多元统计分析期末试题

一、简答题 1、试述距离判别法、Fisher判别法和贝叶斯判别法的异同。 二、 2、设 X {X} X~ N 2 {N_2} N2​(μ&#xff0c;Σ)&#xff0c;其中 X {X} X ~ ( X 1 {X_1} X1​, X 2 {X_2} X2​, X 3 {X_3} X3​)&#xff0c;μ ( μ 1 {μ_1} μ1​&#xff0c; μ 2 {μ_2} …

2024不收费的数据恢复软件EasyRecovery16

EasyRecovery2024是一款操作安全、用户可自主操作的数据恢复方案&#xff0c;它支持从各种各样的存储介质恢复删除或者丢失的文件&#xff0c;其支持的媒体介质包括&#xff1a;硬盘驱动器、光驱、闪存、硬盘、光盘、U盘/移动硬盘、数码相机、手机以及其它多媒体移动设备。能恢…

软件测试【理论基础】

软件测试的IEEE定义&#xff1a;使用人工或自动的手段来运行或测量软件系统的过程&#xff0c;目的是检验软件系统是否满足规定的需求&#xff0c;并找出与预期结果之间的差异。 软件测试的发展趋势&#xff1a; ① 测试工作将进一步前移。软件测试不仅仅是单元测试、集成测…

数据结构算法-插入排序算法

引言 玩纸牌 的时候。往往 需要将牌从乱序排列变成有序排列 这就是插入排序 插入排序算法思想 先看图 首先第一个元素 我默认已有序 那我们从第二个元素开始&#xff0c;依次插入到前面已有序的部分中。具体来说&#xff0c;我们将第二个元素与第一个元素比较&#xff0c;…

单细胞测序并不一定需要harmony去除批次效应

大家好&#xff0c;今天我们分享的是单细胞的学习教程https://www.singlecellworkshop.com/analysis-tutorial.html 教程的作者使用了四个样本&#xff0c;但是没有使用harmony或者其他方法去整合 去除批次效应。 主要内容&#xff1a; SCTransform流程代码及结果 harmony流程…

Shell脚本介绍

Shell脚本是一种使用文本编辑器编写的简单脚本语言&#xff0c;它可以自动化常见的系统任务&#xff0c;例如执行命令、处理文件和文本数据等。Shell脚本通常使用Unix或Linux系统中的shell&#xff08;例如bash&#xff09;来解释执行。 Shell脚本的基本语法包括&#xff1a; …

scrapy的建模及管道的使用

一、数据建模 通常在做项目的过程中&#xff0c;在items.py中进行数据建模 为什么建模 定义item即提前规划好哪些字段需要抓&#xff0c;防止手误&#xff0c;因为定义好之后&#xff0c;在运行过程中&#xff0c;系统会自动检查&#xff0c;配合注释一起可以清晰的知道要抓…

【面试经典150 | 二叉树】二叉树的最大深度

文章目录 写在前面Tag题目来源解题思路方法一&#xff1a;递归方法二&#xff1a;迭代 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算法&#xff0c;两到三天更新一篇文章&#xff0c;欢迎催更…… 专栏内容以分析题目为主&#xff0c;并附带一些对于本题涉及到的…

MVSNeRF:多视图立体视觉的快速推广辐射场重建

MVSNeRF&#xff1a;多视图立体视觉的快速推广辐射场重建 摘要1 引言 摘要 在2021年&#xff0c;作者提出了MVSNeRF&#xff0c;一种新的神经渲染方法&#xff0c;在视图合成中可以有效地重建神经辐射场。与之前对神经辐射场的研究不同&#xff0c;我们考虑了对密集捕获的图像…

十分钟带你看懂——Python测试框架之pytest最全讲

pytest特短 pytest是一个非常成熟的全功能的Python测试框架&#xff0c;主要有以下几个特点&#xff1a; 简单灵活&#xff0c;容易上手 支持参数化 能够支持简单的单元测试和复杂的功能测试&#xff0c;还可以用来做selenium/appnium等自动化测试、接口自动化测试&#xff08…

如何能够对使用ShaderGraph开发的Shader使用SetTextureOffset和SetTextureScale方法

假设在ShaderGraph中的纹理的引用名称为"_BaseMap"&#xff0c;同时对这个"_BaseMap"纹理使用了采样的节点"SampleTexture2D"&#xff0c;然后该采样节点的uv接入的TilingAndOffset节点&#xff0c;此时的关键步骤是新建一个Vector4属性&#xf…

C++实现顺序栈的基本操作(扩展)

#include <stdio.h> typedef char ElemType; #define StackSize 100 /*顺序栈的初始分配空间*/ typedef struct { ElemType data[StackSize]; /*保存栈中元素*/int top; /*栈顶指针*/ } SqStack; void InitStack(SqStack &st) {st.top-1; } …

SSM整合(注解版)

SSM 整合是指将学习的 Spring&#xff0c;SpringMVC&#xff0c;MyBatis 进行整合&#xff0c;来进行项目的开发。 1 项目基本的配置类 1.1 Spring 配置类 这个配置类主要是管理 Service 中的 bean&#xff0c;controller 层的 bean 对象是 SpringMVC 管理的 package cn.ed…