【进程间通信】——共享内存

news/2025/1/20 5:55:54/文章来源:https://www.cnblogs.com/Mr--Song/p/18239025

目录
  • 共享内存 (Shared Memory)
    • 前言
      • 虚拟内存
      • 驻留内存
    • System V 共享内存
      • 函数及其用途

Unix系统的System-V版本中就引入了三种进程间通信方式,分别是消息队列、共享内存、信号量集。这三种通信方式也被称为System-V IPC对象。

共享内存 (Shared Memory)

前言

​ 在下文或接下来的讨论中,我将借用了大佬的文章来深化对特定知识点的理解。我非常尊重原创内容和知识产权,如果有任何侵犯版权或不符合规范的地方,请不吝告知。我将立即采取措施进行修正,并确保内容的合法合规使用。感谢您的理解与支持。

在理解共享内存之前,想必在Linux上写过程序的同学之前都会有这么个疑问,你写的程序在运行时占用了多少内存(物理内存)?

通常我们可以通过top命令查看进程占用了多少内存。

image

这里我们深入探讨一下Linux系统中的VIRTRESSHR这三个关键性能指标,它们各自具有独特的含义。

top命令是Linux系统中一个功能强大的性能监控工具,它不仅可以实时显示系统的总体性能,还能深入到每个进程层面,提供详细的CPU使用率、内存使用情况等信息。通过top命令,我们可以轻松获取到每个进程的VIRTRESSHR等关键指标,从而对系统的性能状况有一个全面的了解。

虚拟内存

​ 首先,我们需要明确区分两个概念:虚拟内存物理内存。虚拟内存是操作系统内核为了管理进程地址空间而设计的一个逻辑上的概念,而物理内存则是实际存在于计算机硬件中的存储空间。一个程序可能拥有很大的虚拟内存空间,但这并不意味着它必然占用大量的物理内存。这是因为虚拟内存空间允许程序访问比物理内存更大的地址范围,而操作系统会根据需要将虚拟内存中的数据实际加载到物理内存中。

虚拟内存的概念至关重要。当我们编写程序,例如C语言程序,并使用gcc编译器进行编译时,编译器分配给程序的地址实际上是虚拟内存空间中的地址。在程序运行之前,这些地址并不对应物理内存中的任何位置。程序运行过程中可能需要的所有指令和数据,都必须首先存在于虚拟内存空间中。

​ 既然虚拟内存是一个逻辑上的概念,为了让程序能够在物理机器上执行,就必须有一种机制将这些逻辑上的虚拟内存映射到物理内存上(实实在在的RAM内存条上的空间)。这就是操作系统中的页映射表发挥作用的地方。操作系统为系统中的每个进程维护一个独立的页映射表。

​ 页映射表的工作原理是将程序在运行时需要访问的虚拟内存空间的各个部分,通过页映射表映射到物理内存空间的相应部分。这样,当CPU需要访问某个虚拟内存地址时,它可以通过查找页映射表来找到对应的物理内存地址。这个过程是Linux操作系统分页机制的核心,其中“页(page)”是虚拟内存空间向物理内存空间映射的基本单位。通过这种方式,操作系统能够高效地管理内存资源,确保程序的顺利运行。

image
​ 图中演示了虚拟内存空间和物理内存空间的映射关系,它们通过 Page Table关联起来,其中虚拟内存空间中着色的部分分别被映射到物理内存空间对应相同着色的部分。而虚拟内存空间中灰色的部分表示在物理内存空间中没有与之对应的部分,也就是说灰色部分没有被映射到物理内存空间中。这么做也是本着“按需映射”的指导思想,因为虚拟内存空间很大,可能其中很多部分在一次程序运行过程中根本不需要访问,所以也就没有必要将虚拟内存空间中的这些部分映射到物理内存空间上。总结一下就是,虚拟内存是一个假象的内存空间,在程序运行过程中虚拟内存空间中需要被访问的部分会被映射到物理内存空间中。虚拟内空间大只能表示程序运行过程中可访问的空间比较大,不代表物理内存空间占用也大。

VIRT表示的是进程虚拟内存空间大小。对应到图中的进程A来说就是A1、A2、A3、A4以及灰色部分所有空间的总和。也就是说VIRT包含了在已经映射到物理内存空间的部分和尚未映射到物理内存空间的部分和。RES表示的是进程虚拟內存空间中已经映射到物理內存空间的那部分的大小。对应到图中的进程A来说就是A1、A2、A3以及A4几个部分空间的总和。所以说,看进程在运行过程中占了多少内存应该看RES的值而不是VIRT的值。SHR是 share(共享)的缩写,它表示的是进程占用的共享内存大小。在上图中我们看到进程A虚拟内存空间中的A4和进程B虚拟内存空间中的B3都映射到了物理内存空间的A4/B3。为什么会出现这样的情况呢?

​ 其实我们写的程序会依赖于很多外部的动态库(.so),比如libc.so、libd.so等等。这些动态库在内存中仅仅会保存/映射一份,如果某个进程运行时需要这个动态库,那么动态加载器会将这块内存映到对应进程的虚拟内存空间中。多个进程之间通过共享内存的方式相互通信也会出现这样的凊况。这么一来,就会出现不同进程的虚拟内存空间会映射到相同的物理内存空间。这部分物理内存空间其实是被多个进程所共享的,某个进程占用的内存除了和别的进程共享的内存之外就是自己的独占内存了。所以要计算进程独占内存的大小只要用RES的值减去SHR值即可 。

驻留内存

​ 顾名思义是指那些被映射到进程虛拟内存空间的物理内存。上图中,在系统物理内存空间中被着色的部分都是驻留内存。比如,A1、A2、A3和A4是进程A的驻留内存,B1、B2和B3是进程B的驻留内存。进程的驻留内存就是进程实实在在占用的物理内存。一般我们所讲的进程占用了多少内存,其实就是说的占用了多少驻留内存而不是多少虚拟内存。因为虛拟内存大并不意味着占用的物理内存大。

System V 共享内存

​ 从上面已经知道了共享内存其实是物理内存中的一段内存空间。所以进程想要一块共享内存必须向操作系统申请一块物理内存,然后映射到虚拟内存。而Linux系统也提供了一系列有关共享内存的函数接口,如 shmget, shmat, shmdt, shmctl 等函数。

函数及其用途

  • shmget

    • 用途:用于创建一个新的共享内存段或获取一个已存在的共享内存段的标识符;

    • 原型int shmget(key_t key, size_t size, int shmflg);

    • **@param **

      • key_t key: 用于标识共享内存段的键值。可以使用 ftok 函数生成,也可以自己定义。如果使用 IPC_PRIVATE 作为键值,则会创建一个新的共享内存段,且只有调用 shmget 的进程能访问。

      • size_t size: 共享内存段的大小(以字节为单位)。如果共享内存段已经存在,此参数可以为 0。

      • int shmflg: 标志位,控制共享内存段的创建和权限。常用标志包括:

        • IPC_CREAT: 如果共享内存段不存在,则创建一个新的。

        • IPC_EXCL: 与 IPC_CREAT 一起使用,如果共享内存段已经存在,则返回错误。

        • 权限标志:如 0666,表示读写权限。

    • 示例

key_t key = ftok("shmfile", 66);
if (key == -1) {fprintf(stderr, "ftok error,errno:%d,%s\n", errno, strerror(errno));exit(EXIT_FAILURE);
}
int shmid = shmget(key, size, IPC_CREAT | IPC_EXCL | 0666);
if (shmid == -1) {fprintf(stderr, "shmget error,errno:%d,%s\n", errno, strerror(errno));exit(EXIT_FAILURE);
}

注意:申请成功的共享内存段里面存储的内容会被自动初始化为0,并且内核会为每一块创建的共享内存分配一个shmid_ds结构体来记录共享内存的属性和信息。

  • shmat

    • 用途:用于将共享内存段映射到调用进程的虚拟地址空间;

    • 原型void *shmat(int shmid, const void *shmaddr, int shmflg);

    • @param

      • int shmid: 共享内存段的标识符,由 shmget 返回。

      • const void *shmaddr: 指定进程附加共享内存段的地址。通常设为 NULL,让系统自动选择合适的地址。

      • int shmflg: 标志位,控制共享内存段的附加行为。常用标志包括:

        • SHM_RDONLY: 只读模式附加共享内存段。

        • 0: 读写模式附加共享内存段。

char *shmaddr = (char *)shmat(shmid, NULL, 0);
if (shmaddr == (char *)(-1)) {fprintf(stderr, "shmat error, errno: %d, %s\n", errno, strerror(errno));exit(EXIT_FAILURE);
}

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

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

相关文章

对题目集4~6的总结

1.前言答题判题程序-4是前几次题目迭代,在前几次的基础上新增加了选择、填空题,按照之前的思路拓展即可。 家居强电电路模拟程序-1、2总体难度适中,不过与普通的信息处理相比,分配电压要考虑更多情况。2.设计与分析答题判题程序-4点击查看题目 设计实现答题程序,模拟一个小…

Linux之系统故障汇总

一、系统可能会出现的故障 1、管理员密码忘记 2、系统无法正常启动grub损坏(MBR损坏、grub配置文件丢失) 系统初始化故障(某文件系统无法正常挂载、驱动不兼容) 服务故障 用户无法登录系统(bash程序故障)3、命令无法运行 4、编译过程无法继续(开发环境缺少基本组件) 二、单用户…

Microsoft 收集分享

链接:https://pan.baidu.com/s/13Lw4B6Qxq5Y65sAXwWOXYA?pwd=l9wj Windows Server 2022SW_DVD9_Win_Server_STD_CORE_2022_2108.33_64Bit_English_DC_STD_MLF_X23-75607.ISO SHA256:5B4721A21DF2E2D7C20803BB9D64C2B5BF6ED72E612480CF9298969001B65CE8 updated May 2024SW_DV…

一文搞懂 ARM 64 系列: 寄存器

一文搞懂 ARM 64 系列: 寄存器ARM 64中包含多种寄存器,下面介绍一些常见的寄存器。 1 通用寄存器 ARM 64包含31个64bit寄存器,记为X0~X30。 每一个通用寄存器,它的低32bit都可以被访问,记为W0~W30。在这31个通用寄存器中,有2个寄存器比较特殊。 X29寄存器被作为栈帧寄存器…

Vue第三方库与插件实战手册

这篇文章介绍了如何在Vue框架中实现数据的高效验证与处理,以及如何集成ECharts、D3.js、Chart.js等图表库优化数据可视化效果。同时,探讨了Progressive Web App(PWA)的接入与优化策略,以提升Web应用的用户体验与加载速度。title: Vue第三方库与插件实战手册 date: 2024/6/8 …

JPG的解码流程

JPG的解码流程创建解码对象,并且对解码对象进行初始化,创建错误处理对象,并和解码对象进行关联。打开待解码的jpg图片,以二进制方式打开文件读取待解码的jpg图像信息设置解码参数(可选) 开始解码循环读取解码对象中的颜色分量(以行扫描)解码完成,释放解码

C语言实验六

#include <stdio.h> #include <string.h> #define N 3 // 运行程序输入测试时,可以把这个数组改小一些输入测试 typedef struct student {int id; // 学号 char name[20]; // 姓名 char subject[20]; // 考试科目double perf; …

oracle 表管理

主键自增策略 参考: Oracle实现主键自增有4种方式 Oracle 自增长主键 三种方式 Oracle中sequence(序列)详解 mybatis+oracle数据库新增数据,返回主键,主键回显 oracle 数据类型 Oracle中的数据类型详解 创建表 create table sys_dict_type( id number(20) primary key, nam…

6.8哈希表

1. 两数之和 题意描述:给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。 你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。 你可以按任意顺序返回答案。 示例…

2024-06-08:用go语言,给定三个正整数 n、x和y, 表示城市中的房屋数量以及编号为x和y的两个特殊房屋。 在这座城市中,房屋通过街道相连。对于每个编号i(1 <= i < n), 存在一条

2024-06-08:用go语言,给定三个正整数 n、x和y, 表示城市中的房屋数量以及编号为x和y的两个特殊房屋。 在这座城市中,房屋通过街道相连。对于每个编号i(1 <= i < n), 存在一条连接第i个房屋与第(i+1)个房屋的街道。 此外,还有一条特殊街道连接编号为x的房屋与编号…

特别好的一个网站。各种JAVA JDK的镜像分发。

WEJDK学习站 (injdk.cn) https://www.injdk.cn/