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_CREAT
                        IPC_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.mfbz.cn/a/337056.html

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

相关文章

web漏洞总结大全(基础)

前言 本文章是和cike_y师傅一起写的&#xff0c;cike_y博客&#xff1a;https://blog.csdn.net/weixin_53912233?typeblog 也欢迎大家对本文章进行补充和指正&#xff0c;共同维护这个项目&#xff0c;本文的github项目地址&#xff1a; https://github.com/baimao-box/Sum…

Golang 搭建 WebSocket 应用(八) - 完整代码

本文应该是本系列文章最后一篇了&#xff0c;前面留下的一些坑可能后面会再补充一下&#xff0c;但不在本系列文章中了。 整体架构 再来回顾一下我们的整体架构&#xff1a; 在我们的 demo 中&#xff0c;包含了以下几种角色&#xff1a; 客户端&#xff1a;一般是浏览器&am…

java枚举详细解释

枚举的基本认识 我们一般直接定义一个单独的枚举类 public enum 枚举类名{枚举项1,枚举项2,枚举项3 } 可以通过 枚举类名.枚举项 来访问该枚举项的 - 可以理解为 枚举项就是我们自己定义的一个数据类型,是独一无二的 接下来我们直接用一个例子来完全理解 加深理解 这里…

Linux-nginx(安装配置nginx、配置反向代理、Nginx配置负载均衡、动静分离)

关于代理 正向代理: 客户明确知道自己访问的网站是什么 隐藏客户端的信息 目录 关于代理 一、Nginx的安装与配置 1、安装依赖 2、安装nginx &#xff08;1&#xff09;上传压缩包到目录 /usr/nginx里面 &#xff08;2&#xff09;解压文件 &#xff08;3&#xff09…

String在VS与Linux下的区别

目录 一、string的成员 1.VS 2.Linux 二、string的扩容机制 1. VS 2.Linux 一、string的成员 string是C标准库中的一个类模板&#xff0c;用于表示和操作字符串 string在 Windows 与 Linux 中的成员不是相同的 1.VS 4个成员&#xff1a;_str , _size , _capacity 和…

【一文详解】Java多线程和并发知识点详细总结【万字总结】

Java并发编程 并发编程的三个特性 原子性 一次操作或者多次操作&#xff0c;要么所有的操作全部都得到执行并且不会受到任何因素的干扰而中断&#xff0c;要么都不执行。 在 Java 中&#xff0c;可以借助synchronized、各种 Lock 以及各种原子类实现原子性。 synchronized…

MySQL(五)——多表查询

上期文章 MySQL&#xff08;四&#xff09;——约束 文章目录 上期文章多表关系一对多&#xff08;多对一&#xff09;多对多多表外键关系可视化一对一 多表查询概述笛卡尔积多表查询分类连接查询 内连接隐式内连接显式内连接 外连接左外连接右外连接 自连接联合查询 union&am…

python-基础篇-变量

文章目录 变量的基本使用目标01. 变量定义1) 变量演练1 —— iPython2) 变量演练 2 —— PyCharm3) 变量演练 3 —— 超市买苹果思考题 02. 变量的类型2.1 变量类型的演练 —— 个人信息2.2 变量的类型2.3 不同类型变量之间的计算1) **数字型变量** 之间可以直接计算2) **字符串…

Python基础第四篇(Python函数)

文章目录 一、函数介绍二、函数的定义三、函数的参数与返回值四、函数说明文档五、函数的嵌套六、变量域七、函数案例1.源代码2.读出结果 在程序设计领域&#xff0c;函数成为一个不可或缺的角色&#xff0c;它们为我们提供了精练、高效和易于管理的编程方式。本篇博客将带您深…

CentOS 7安装Java并配置环境

一、安装Java环境 1、检查系统是否安装Java [rootlocalhost ~]# java -version 2、更新系统软件包 [rootlocalhost ~]# yum update #遇到[y/n],选择y并回车&#xff0c;耐心等待下载完毕&#xff0c;之后系统会自动检验更新的软件包遇到 /var/run/yum.pid 已被锁定 /var/…

【动态规划】【数学】【C++算法】805 数组的均值分割

作者推荐 【动态规划】【数学】【C算法】18赛车 本文涉及知识点 动态规划 数学 805 数组的均值分割 给定你一个整数数组 nums 我们要将 nums 数组中的每个元素移动到 A 数组 或者 B 数组中&#xff0c;使得 A 数组和 B 数组不为空&#xff0c;并且 average(A) average(B)…

nuclei安装;linux上 以及使用教程

kali安装go环境_go1.17 kali安装-CSDN博客Ubuntu完美解决Github网站打不开问题 - 一抹烟霞 - 博客园 (cnblogs.com) All releases - The Go Programming Language 然但是上面两个我似乎都没用到网上的教程 也不适用 一个网不好 一个apt没找到包 然后我先试试了版本 结果 我的…

BGP Origin 属性控制选路试验

一、拓朴图&#xff1a; 二、配置步骤&#xff1a; 1、配置 IP 2、配置 IGP&#xff0c;我们这里用了静态&#xff0c;互相宣告了对端接口和 Loopback 0 3、配置 BGP 4、在 R1 上通过 BGP 宣告 1.1.1.1&#xff0c;查看 R2 的路由&#xff0c;发现两条 1.1.1.1 的路由&#x…

Vue中的组件

在应用程序的开发中&#xff0c;组件是不可缺少的。在Vue的使用中&#xff0c;同样也会用到组件。   vue组件的一般知识点&#xff1a;   1、组件的名字唯一&#xff1b;   2、组件以Html形式书写&#xff1b;   3、组件可以复用&#xff1b;   4、组件可以嵌套&…

postgresql(Windows)初始化数据库教程

省流&#xff1a;本文章内容讲的是如何初始化postgresql数据库环境&#xff0c;前提是已经安装好postgresql数据库&#xff0c;安装步骤参考postgresql&#xff08;Windows&#xff09;安装教程 # 开始&#xff1a;安装postgresql-12.14-2-windows-x64.exe完成后进行初始化数据…

gin中间件篇

1. 全局中间件 所有请求都经过此中间件 package mainimport ("fmt""time""github.com/gin-gonic/gin" )// 定义中间 func MiddleWare() gin.HandlerFunc {return func(c *gin.Context) {t : time.Now()fmt.Println("中间件开始执行了&quo…

《Linux高性能服务器编程》笔记04

Linux高性能服务器编程 本文是读书笔记&#xff0c;如有侵权&#xff0c;请联系删除。 参考 Linux高性能服务器编程源码: https://github.com/raichen/LinuxServerCodes 豆瓣: Linux高性能服务器编程 文章目录 Linux高性能服务器编程第09章I/O复用9.1 select系统调用9.2 po…

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 本地方法栈 版权声明 本博客的内容基于我个人学习黑马程…

如何快速打开github

作为一个资深码农&#xff0c;怎么能不熟悉全球最大的同性交友社区——github呢&#xff0c;但头疼的是github有时能打开&#xff0c;有时打不开&#xff0c;这是怎么回事&#xff1f; 其实问题出在github.com解析DNS上&#xff0c;并不是需要FQ。下面提供一个方法&#xff0c;…

C++:基于C的语法优化

C&#xff1a;基于C的语法优化 命名空间命名空间域域作用限定符展开命名空间域 输入输出缺省参数全缺省参数半缺省参数 函数重载参数类型不同参数个数不同参数类型的顺序不同 引用基本语法按引用传递返回引用引用与指针的区别 内联函数autoauto与指针和引用结合 范围for循环nul…