Linux共享内存 和相关的 shm函数 shmget,shmat,shmdt,shmctl函数

article/2024/7/17 22:06:11

目录

    • 一、什么是共享内存
    • 二、使用共享内存的准备和收尾工作
    • 三、shmget函数(shared memory get)
    • 四、关联函数shmat
    • 五、解除函数shmdt
    • 六. shmctl函数,删除共享内存
    • 七、相关shell命令
    • 八、共享内存的状态

一、什么是共享内存

1、共享内存的定义
由于进程通信的本质是要让两个不同的进程看到同一份资源,我们可以在物理内存上开辟一块空间,这块空间被称为共享内存,然后让这两个进程通过某种方式都能访问到这块内存,这样的话,两个进程之间就可以通信了。

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

2、共享内存的特点
第一,和创建进程类似,进程被创建的时候,会被分配一个pid来标识这个进程的唯一性,同时也方便OS管理这些进程,因此共享内存在被创建的时候,会被分配一个“ID”来标识唯一性。

第二,共享内存可以允许存在多个,为了区分这些共享内存,我们上面引入了“ID”的概念,但是要如何让两个进程连上同一个共享内存呢??

就好比,我要和人solo(通信),我创建了一个房间(共享内存),这个房间就有了房间号(共享内存的ID),是个人都能进这个房间,根本没法通信,所以我们要设置房间密码。因此为了通信,我们需要两样东西,一个是房间号,一个是房间密码

二、使用共享内存的准备和收尾工作

当我们需要使用共享内存时,我们要做的准备工作是:
a.通过某种调用,在内存中开辟一块空间(shmget)
b.通过某种调用,让两个进程挂接到这个新开辟的空间上(shmat)

当我们不需要使用共享内存时,我们需要做的收尾工作是:
a. 断开进程和共享内存之间的关联(shmdt)
b.释放共享内存(shmctl)
每一步都对应着一个系统调用接口,下面要说的就是这四个系统调用接口。

三、shmget函数(shared memory get)

这是共享内存的创建函数,调用以后会向内核申请内存,但是需要注意的是,共享内存是以“页”为单位的,一页是4KB = 4096bytes,所以一般建议申请共享内存的大小是4KB的整数倍!

如果申请了4097个字节,那么OS会给你4096*2个字节的空间。
函数原型:
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);(1) 第一个参数 key
第一个参数是唯一标识编号,也就是前面说到的房间密码,这个是由用户自己设置的,一般是通过ftok函数来,也可以自己随意设置一个整数。
// ftok函数原型
#include <sys/types.h>
#include <sys/ipc.h>
// 将两个参数作为种子, 生成一个 key_t 类型的数值
key_t ftok(const char *pathname, int proj_id)ftok函数的第一个参数:路径名
ftok函数的第二个参数:项目ID
关于这个函数无需想的太复杂,简单来说就是,从路径名中取出一部分,然后再从ID中取出一部分,最后再把两部分组合一下形成一个整数,我们就把这个整数当作“房间密码”。
注意:ftok被不同进程调用,只要路径名和ID是一样的,生成的整数就是一样的。(2) 第二个参数 size
第二个参数是开辟共享内存的大小,一般建议是4KB的整数倍(原因详见第三部分开头)。(3) 第三个参数 shmget
第三个参数是创建共享内存的方式以及设置权限。属于位运算输入
IPC_CREAT:可以单独使用,如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内存的ID;如果已经存在,则沿用已有的共享内存,函数返回值是已有的共享内存的 ID。
IPC_EXCL:检测共享内存是否已经存在了,无法单独使用,要配IPC_CREAT使用,即 IPC_CREAT | IPC_EXCL。IPC_CREAT | IPC_EXCL:如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内存的ID;如果已经存在,则报错。
IPC_CREAT | IPC_EXCL | 0664:开辟共享内存的同时,设置共享内存的访问权限.(4)返回值解析
如果共享内存开辟成功,则返回共享内存的ID(即房间号);否则返回 -1

四、关联函数shmat

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

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

五、解除函数shmdt

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

int shmdt(const void *shmaddr);
参数:shmat () 函数的返回值,共享内存的起始地址
返回值:关联解除成功返回 0,失败返回 - 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:
cmdIPC_STAT, 作为传出参数,会得到共享内存的相关属性信息
cmdIPC_SET, 作为传入参,将用户的自定义属性设置到共享内存中
cmd==IPC_RMID, buf 就没意义了,这时候 buf 指定为 NULL 即可
返回值:函数调用成功返回值大于等于 0,调用失败返回 - 1

七、相关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 key

id == shmget的返回值
$ ipcrm -m id

八、共享内存的状态

// 参数 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,其属性从公共的变为私有的。这里的私有是指只有已经关联成功的进程才允许继续访问共享内存,不再允许新的进程和这块共享内存进行关联了。下图演示了共享内存的状态变化:


http://www.ngui.cc/article/show-1200642.html

相关文章

VMware ESXi 8.0b Unlocker OEM BIOS 集成 REALTEK 网卡驱动和 NVMe 驱动 (集成驱动版)

VMware ESXi 8.0b Unlocker & OEM BIOS 集成 REALTEK 网卡驱动和 NVMe 驱动 (集成驱动版) 发布 ESXi 8.0 集成驱动版&#xff0c;在个人电脑上运行企业级工作负载 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-8-sysin/&#xff0c;查看最新版。原创作…

当你怀疑电脑的时候,就是该换个方法的时候了

做软件开发的朋友们&#xff0c;干的时间长了&#xff0c;相信都会有过类似的体验&#xff1a;一个问题查了很久很久&#xff0c;代码检查了很多遍很多遍&#xff0c;到最后都要怀疑电脑了&#xff0c;突然发现是一个特别隐蔽的错误导致的&#xff0c;而这种错误通常不涉及逻辑…

AtCoder Beginner Contest 303——A-E题讲解

蒟蒻来讲题&#xff0c;还望大家喜。若哪有问题&#xff0c;大家尽可提&#xff01; Hello, 大家好哇&#xff01;本初中生蒟蒻讲解一下AtCoder Beginner Contest 303这场比赛的A-E题&#xff01; A - Similar String 原题 Problem Statement Two characters x x x and y…

VUE SAP、 MPA,,组件开发、VDOM、双向数据绑定

单页面应用&#xff08;spa&#xff09; 概念&#xff1a;只有一个html页面&#xff0c;所有跳转方式都是通过组件切换完成的。 优点&#xff1a;页面之间跳转流畅、组件化开发、组件可复用、开发便捷、易维护。 缺点&#xff1a;首屏加载较慢&#xff0c;加载整个项目中使用的…

Android Jetpack组件库(第七部分)---UI工具包 Compose

Android Jetpack 是 Google 推出的一整套帮助 Android 应用程序开发的库、工具包和架构指南&#xff0c;旨在为 Android 应用程序提供更快&#xff0c;更轻松&#xff0c;更稳定的开发体验。自推出以来已经发展成了一个庞大的技术生态系统&#xff0c;包括了许多使用方便、功能…

单例模式8种写法

0. 为什么需要单例模式&#xff1f; 节省内存和计算保证结果正确方便管理 使用场景&#xff1a; 1. 饿汉式&#xff08;静态常量&#xff09;—推荐指数&#xff1a;★★☆☆☆ 优点&#xff1a;不会有线程安全问题。 缺点&#xff1a;在类加载的时候就创建对象&#xff0c;…

CentOS安装jdk、tomcat、apache

一、安装JDK8 centos 创建/home/software文件夹 安装好xshell与xftp并连接centos jdk官网https://www.oracle.com/java/technologies/downloads/#java8 下载 上传/home/software 解压缩 tar -zxvf jdk-8u371-linux-x64.tar.gz 重命名 mv jdk1.8.0_371 jdk8添加环境变量…

美国金融科技公司SoFi的增长难以持久,股价也将下跌

来源&#xff1a;猛兽财经 作者&#xff1a;猛兽财经 公司介绍 SoFi Technologies(SoFi)是一家来自美国的知名金融科技公司&#xff0c;自2011年成立以来&#xff0c;已成为领先的个人理财在线平台。SoFi为年轻的高收入客户提供多样化的产品和服务&#xff0c;包括学生和汽车贷…

sql server 字符串链接,及表连接多个值显示连接显示为一列 STUFF for xml path

sql server 字符串链接&#xff0c;及表连接多个值显示连接显示为一列 STUFF for xml path STUFF ( character_expression , start , length , replaceWith_expression ) 以下示例从第一个字符串 abcdef 的第 2 个位置 (b) 开始删除三个字符&#xff0c;然后在删除位置插入…

I.MX RT1170加密启动详解(4):OTFAD XIP加密运行代码

本节将介绍基于AES加密的OTFAD引擎&#xff0c;它可以在不影响AES-128-CTR性能的情况下实时解密数据。OTFAD包括对AES密钥展开机制的完整硬件支持&#xff0c;它可以解密最多4个唯一的AES上下文。每个上下文都有一个用户定义的128位的Image Encryption Key(IEK)、一个64位的计数…