Linux中的共享内存

定义:

共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种 IPC 机制无需内核介入。所有需要做的就是让一个进程将数 据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。
与管道等要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种 IPC 技术的速度更快。

使用步骤:

1. 调用 shmget() 创建一个新共享内存段或取得一个既有共享内存段的标识符(即由其他进程创建的共享内存段)。这个调用将返回后续调用中需要用到的共享内存标识符
2. 使用 shmat() 来附上共享内存段,即使该段成为调用进程的虚拟内存的一部分。
此刻在程序中可以像对待其他可用内存那样对待这个共享内存段。为引用这块共享内存,程序需要
3. 使用由 shmat() 调用返回的 addr 值,它是一个指向进程的虚拟地址空间中该共享内存段的起点的指针。
4. 调用 shmdt() 分离共享内存段。在这个调用之后,进程就无法再引用这块共享内存了。这一步是 可选的,并且在进程终止时会自动完成这一步。
5. 调用 shmctl() 删除共享内存段(前面的分离操作只是这个进程不能使用共享内存,但是这块共享内存仍然存在)。只有当当前所有附加内存段的进程都与之分离之后内存段才会销毁。只有一个进程需要执行这一步。(当还有进程共享内存时,删除共享内存段是不会成功的。因为还有别的进程在使用共享内存段。)

相关函数:

shmget():

#include <sys/ipc.h>
#include <sys/shm.h>int shmget(key_t key, size_t size, int shmflg);- 功能:创建一个新的共享内存段,或者获取一个既有的共享内存段的标识。新创建的内存段中的数据都会被初始化为0- 参数:- key : key_t类型是一个整形,通过这个找到或者创建一个共享内存。一般使用16进制表示,非0值- size: 共享内存的大小(以分页的大小去创建的,也就是第一个大于传入的大小的分页大小去创建)- shmflg: 属性- 访问权限- 附加属性:创建/判断共享内存是不是存在- 创建:IPC_CREATIPC_CREAT 是一个用于创建共享内存的标志,指定了共享内存的权限。如果共享内存已经存在,该标志会被忽略。- 判断共享内存是否存在: IPC_EXCL , 需要和IPC_CREAT一起使用使用例子:IPC_CREAT | IPC_EXCL | 0664- 返回值:失败:-1 并设置错误号成功:> 0 返回共享内存的引用的ID,后面操作共享内存都是通过这个值。

shmat():

#include <sys/ipc.h>
#include <sys/shm.h>void *shmat(int shmid, const void *shmaddr, int shmflg);- 功能:和当前的进程进行关联- 参数:- shmid : 共享内存的标识(ID),由shmget返回值获取- shmaddr: 申请的共享内存的起始地址(虚拟内存当中的地址),指定NULL,内核指定- shmflg : 对共享内存的操作- 读 : SHM_RDONLY, 必须要有读权限- 读写: 0 (必须要有读权限和写权限才能去操作共享内存)- 返回值:成功:返回共享内存的首(起始)地址。  失败(void *) -1

shmdt():

#include <sys/ipc.h>
#include <sys/shm.h>int shmdt(const void *shmaddr);- 功能:解除当前进程和共享内存的关联- 参数:shmaddr:共享内存的首地址- 返回值:成功 0, 失败 -1

shmctl():

#include <sys/ipc.h>
#include <sys/shm.h>int shmctl(int shmid, int cmd, struct shmid_ds *buf);- 功能:对共享内存进行操作。主要用来删除共享内存,共享内存要删除才会消失,创建共享内存的进行被销毁了对共享内存是没有任何影响。- 参数:- shmid: 共享内存的ID- cmd : 要做的操作- IPC_STAT : 获取共享内存的当前的状态- IPC_SET : 设置共享内存的状态- IPC_RMID: 标记共享内存被销毁- buf:需要设置或者获取的共享内存的属性信息- IPC_STAT : buf存储数据- IPC_SET : buf中需要初始化数据,设置到内核中- IPC_RMID : 没有用,NULL

key_t ftok():

#include <sys/ipc.h>
#include <sys/shm.h>key_t ftok(const char *pathname, int proj_id);- 功能:根据指定的路径名,和int值,生成一个共享内存的key- 参数:- pathname:指定一个存在的路径/home/nowcoder/Linux/a.txt/ - proj_id: int类型的值,但是这系统调用只会使用其中的1个字节范围 : 0-255  一般指定一个字符 'a'

使用共享内存实现简单的通信:

写程序代码如下:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>int main() {    // 1.创建一个共享内存int shmid = shmget(100, 4096, IPC_CREAT | 0664);// 第一个参数一般给16进制的数printf("shmid : %d\n", shmid);// 2.和当前进程进行关联void * ptr = shmat(shmid, NULL, 0);char * str = "helloworld";// 3.写数据memcpy(ptr, str, strlen(str) + 1);printf("按任意键继续\n");// 不加程序会直接解除关联getchar();// 4.解除关联shmdt(ptr);// 5.删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}

读程序代码如下:

#include <stdio.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <string.h>int main() {    // 1.获取一个共享内存int shmid = shmget(100, 0, IPC_CREAT);// 注意不能大于写文件的4096大小,这里也可以填4096。一般填0表示获取一个共享内存printf("shmid : %d\n", shmid);// 2.和当前进程进行关联void * ptr = shmat(shmid, NULL, 0);// 3.读数据printf("%s\n", (char *)ptr);printf("按任意键继续\n");getchar();// 4.解除关联shmdt(ptr);// 5.删除共享内存shmctl(shmid, IPC_RMID, NULL);return 0;
}

运行结果如下:

 

操作命令:

ipcs 用法:

1. ipcs -a        // 打印当前系统中所有的进程间通信方式的信息
2. ipcs -m      // 打印出使用共享内存进行进程间通信的信息
3. ipcs -q      // 打印出使用消息队列进行进程间通信的信息
4. ipcs -s     // 打印出使用信号进行进程间通信的信息

ipcrm 用法:

1. ipcrm -M shmkey      // 移除用 shmkey 创建的共享内存段
2. ipcrm -m shmid        // 移除用 shmid 标识的共享内存段
3. ipcrm -Q msgkey   // 移除用 msqkey 创建的消息队列
4. ipcrm -q msqid      // 移除用 msqid 标识的消息队列
5. ipcrm -S semkey // 移除用 semkey 创建的信号
6. ipcrm -s semid    // 移除用 semid 标识的信号

一些需要注意的细节:

问题1:操作系统如何知道一块共享内存被多少个进程关联?- 共享内存维护了一个结构体struct shmid_ds 这个结构体中有一个成员 shm_nattch- shm_nattach 记录了关联的进程个数问题2:可不可以对共享内存进行多次删除 shmctl- 可以的- 因为shmctl 标记删除共享内存,不是直接删除- 什么时候真正删除呢?当和共享内存关联的进程数为0的时候,就真正被删除- 当共享内存的key为0的时候,表示共享内存被标记删除了如果一个进程和共享内存取消关联,那么这个进程就不能继续操作这个共享内存。也不能进行关联。

 共享内存和内存映射的区别:

1.共享内存可以直接创建,内存映射需要磁盘文件(匿名映射除外)2.共享内存效率更高3.内存所有的进程操作的是同一块共享内存。内存映射,每个进程在自己的虚拟地址空间中有一个独立的内存。4.数据安全- 进程突然退出共享内存还存在内存映射区消失- 运行进程的电脑死机,宕机了数据存在在共享内存中,没有了内存映射区的数据 ,由于磁盘文件中的数据还在,所以内存映射区的数据还存在。5.生命周期- 内存映射区:进程退出,内存映射区销毁- 共享内存:进程退出,共享内存还在,标记删除(所有的关联的进程数为0),或者关机如果一个进程退出,会自动和共享内存进行取消关联。

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

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

相关文章

力扣精选算法100题——串联所有单词的字串(滑动窗口专题)

本题链接——串联所有单词的字串 本题和找到字符串中所有字母异位词题目非常相似&#xff0c;思路都是一样。通过自己的大脑能发现其中的相似之处。 第一步&#xff1a;了解题意 就按实例来分析吧&#xff0c;这样更通俗易懂。 words["ab","cd","ef…

mysql从库重新搭建的流程

背景 生产环境上的主从集群&#xff0c;因为一些异常原因&#xff0c;导致主从同步失败。现记录下通过重做mysql从库的方式来解决&#xff0c;重做过程不影响主库。 步骤 1、在主库上的操作步骤 备份主库所有数据&#xff0c;并将dump.sql文件拷贝到从库/tmp目录 mysqldump …

Flutter 综述

Flutter 综述 1 介绍1.1 概述1.2 重要节点1.3 移动开发中三种跨平台框架技术对比1.4 flutter 技术栈1.5 IDE1.6 Dart 语言1.7 应用1.8 框架 2 Flutter的主要组成部分3 资料书籍 《Flutter实战第二版》Dart 语言官网Flutter中文开发者社区flutter 官网 4 搭建Flutter开发环境参考…

vue3-模版引用

模版引用 ref 属性 场景&#xff1a;需要直接访问底层 DOM 元素。 方法&#xff1a;使用特殊的 ref 属性。 <input ref"input">ref 属性 允许我们在一个特定的 DOM 元素或子组件实例被挂载后&#xff0c;获得对它的直接引用。 访问模板引用 小 Demo: 当 i…

Go 知识iota

Go 知识iota 1. 介绍2. 特性3. 原理4. 你真的理解了吗 1. 介绍 iota 是一个预定义的标识符&#xff0c;用于声明枚举常量。它在 const 声明中使用&#xff0c;表示连续的未类型化整数。其值从0开始&#xff0c;const声明块每增加一行&#xff0c;iota的值就会自增1&#xff0c…

优化您的服务请求,增强用户体验和服务交付

您的服务请求模板是否像一个复杂的迷宫&#xff0c;给您的团队带来延误和困惑&#xff1f;您的技术人员是否厌倦了为了解最终用户的需求而与他们来回奔波&#xff1f;强大且可定制的请求模板可能正是您所需要的&#xff01; 服务交付团队&#xff08;尤其是 IT&#xff09;的…

javascript入门分享(附:javascript基础入门视频教程)

javascript入门分享&#xff08;附&#xff1a;javascript基础入门视频教程&#xff09; 一、javascript入门了解 JavaScript&#xff08;简称“JS”&#xff09;是一种具有函数优先的轻量级&#xff0c;解释型或即时编译型的编程语言。 虽然它是作为开发Web页面的脚本语言而出…

Node.js Stream.pipeline() Method

Why Stream.pipeline 通过流我们可以将一大块数据拆分为一小部分一点一点的流动起来&#xff0c;而无需一次性全部读入&#xff0c;在 Linux 下我们可以通过 | 符号实现&#xff0c;类似的在 Nodejs 的 Stream 模块中同样也为我们提供了 pipe() 方法来实现。 未使用 Stream p…

基于WSL的Ubuntu命令行美化

大多数 Linux 发行版中的默认 Shell 是 Bash。Bash 缺乏代码高亮&#xff0c;不易阅读。本文旨在通过安装 Zsh、oh-my-zsh&#xff0c;并应用 Powerlevel10k 主题来解决这一问题。 环境&#xff1a;Windows10 Pro 21H2&#xff0c;OS build: 19044.1766&#xff1b;基于 WSL 的…

JVM之java内存区域[1](程序计数器、栈)

文章目录 版权声明零 运行时数据区一 程序计数器1.1 加载阶段1.2 执行阶段1.3 多线程情况 二 栈2.1 java虚拟机栈2.2 java虚拟机栈帧的组成2.2.1 局部变量表2.2.2 操作数栈2.2.3 帧数据 2.3 栈内存溢出2.4 设置帧大小2.5 本地方法栈 版权声明 本博客的内容基于我个人学习黑马程…

Java学习笔记(八)——Lambda表达式

文章目录 Lambda表达式Lambda表达式的省略写法Lambda练习练习1练习2 算法题算法题1 斐波那契数列算法题2 猴子吃桃子算法题3 爬楼梯 Lambda表达式 Lambda表达式是JDK8开始的一种新语法形式。 基本作用&#xff1a;简化函数式接口的匿名内部类的写法。 注意&#xff1a; Lam…

Linux中的新建用户、切换用户

目录 一、Linux系统中有哪些用户 二、新建普通用户 三、root账号与普通账号的切换 一、Linux系统中有哪些用户 1.root 超级管理员&#xff08;不受权限约束&#xff09; 2.其他用户 普通用户&#xff08;受到权限约束&#xff09; 二、新建普通用户 创建新用户 sudo user…