共享内存(一)—— 共享内存的创建与释放

匿名管道和命名管道都是基于文件的进程间通信,SystemV方案是在OS层面专门为进程间通信设计的一个方案,然后通过系统调用(system call)给用户提供通信接口

SystemV方案包含三种:共享内存、消息队列、信号量

下面要说的就是共享内存的创建和释放

目录

一、什么是共享内存??

1、共享内存的定义

2、共享内存的特点

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

三、shmget函数(shared memory get)

1、 参数解析

(1) 第一个参数 key

(2) 第二个参数 size

(3) 第三个参数 shmget

2、返回值解析

3、使用shmget函数

4、共享内存的生命周期

四、shmctl(shared memory control)

1、参数解析

2、返回值解析 

3、使用shmctl 函数

五、为什么共享内存的ID是0,1,2 ... ? 



一、什么是共享内存??

1、共享内存的定义

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

2、共享内存的特点

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

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

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

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

当我们需要使用共享内存时,我们要做的准备工作是:

                — 通过某种调用,在内存中开辟一块空间(shmget)

                — 通过某种调用,让两个进程挂接到这个新开辟的空间上(shmat)

当我们不需要使用共享内存时,我们需要做的收尾工作是:

                — 断开进程和共享内存之间的关联(shmdt)

                — 释放共享内存(shmctl)

每一步都对应着一个系统调用接口,下面要说的就是这四个系统调用接口

三、shmget函数(shared memory get)

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

如果申请了4097个字节,那么OS会给你4096*2个字节的空间

1、 参数解析

下面是shmget函数的声明以及要用到的头文件

(1) 第一个参数 key

第一个参数是唯一标识编号,也就是前面说到的房间密码,这个是由用户自己设置的,一般是通过ftok函数来,也可以自己随意设置一个整数

 ftok函数的第一个参数:路径名

 ftok函数的第二个参数:项目ID

关于这个函数无需想的太复杂,简单来说就是,从路径名中取出一部分,然后再从ID中取出一部分,最后再把两部分组合一下形成一个整数,我们就把这个整数当作“房间密码”

注意:ftok被不同进程调用,只要路径名和ID是一样的,生成的整数就是一样的

(2) 第二个参数 size

 第二个参数是开辟共享内存的大小,一般建议是4KB的整数倍(原因详见第三部分开头)

(3) 第三个参数 shmget

第三个参数是创建共享内存的方式以及设置权限

 类似于write函数的第二个参数,属于位运算输入

IPC_CREAT:可以单独使用,如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内

                        存的ID;如果已经存在,则沿用已有的共享内存,函数返回值是已有的共享内存的

                        ID

IPC_EXCL:无法单独使用,要配合IPC_CREAT使用,即 IPC_CREAT | IPC_EXCL

IPC_CREAT | IPC_EXCL:如果共享内存不存在,则重新开辟,函数返回值是新开辟的共享内

                                        存的ID;如果已经存在,则报错

IPC_CREAT | IPC_EXCL | 0664:开辟共享内存的同时,设置共享内存的访问权限

2、返回值解析

如果共享内存开辟成功,则返回共享内存的ID(即房间号);否则返回 -1.

3、使用shmget函数

下面我们在使用shmget函数的同时要验证一下上面红色的注意事项

和之前的命名管道通信一样,继续使用server.c 和 client.c 来通信,公共数据放在common.h

 现在我们可以来看一下测试的结果,我们会发现服务端和客户端使用的密码是一样的,这也就验证了上面红色部分

          

 我们在命令行输入下面的指令查看创建好的共享内存

ipcs                     #显示SystemV方案的全部,即共享内存、消息队列、信号量
ipcs -m                  # -m 显示共享内存
ipcs -q                  # -q 显示共享消息队列
ipcs -s                  # -s 显示共享信号量

4、共享内存的生命周期

从上面的测试结果,我们会发现,进程退出以后,共享内存依然存在,换到我们之前的匿名管道,管道的生命周期是随进程的,进程退出以后,管道也就不存在了

共享内存不属于任何进程,不归进程管理,除非调用释放函数,否则要不要释放是OS决定的,因此共享内存的生命周期是随内核的!!

释放的方式有三种:

- 关机

- 调用释放共享内存的函数 shmctl

- 命令行释放

ipcrm -m 1             # ipcrm -m 共享内存的id

        

四、shmctl(shared memory control)

这是控制共享内存的函数,目前我们只需要知道,怎么通过这个函数释放共享内存即可

1、参数解析

下面是共享内存控制函数的函数声明及需要的头文件


第一个参数是 共享内存的id

第二个参数是 控制指令(我们选择删除共享内存的那个指令)

第三个参数是 shmid所指向的共享内存的地址,既然空间被释放以后,地址就赋值为null

2、返回值解析 

释放成功则返回 0;释放失败返回 -1

3、使用shmctl 函数

我们在原本的server.c中加入下面的内容

 我们发现,共享内存最终被释放了

五、为什么共享内存的ID是0,1,2 ... ? 

我们观察上面的图,会发现shmid是 0,1,2这样的整数,和前面的文件一样,很容易联想到数组的下标。这确实是数组的下标,但是这个数组中存放着什么呢??

最开始也说了,共享内存以结构体的形式被管理着,消息队列和信号量也是如此,下面依次是共享内存、消息队列、信号量的结构体。

 

 

首先需要明确的是,上面这些共享内存的结构体shmid_ds、消息队列的结构体msgid_ds、信号量的结构体semid_ds都不是关键,关键的是这个ipc_perm

其次,每种资源都嵌套了ipc_perm结构体,我们只要知道了 各自ipc_perm的地址,就能通过C++切片寻找到每种资源的地址

因此,对每种资源结构体的管理就变成了对ipc_perm的管理 ——》 这里的shmid就是这个存储ipc_perm结构体地址的数组下标

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值