☰
Linux进程间通信(五).system Ⅴ共享内存
2026/9/27 22:30:53 网站建设 项目流程

一.何谓system V?

这是一套进程间通信的共享内存的标准。

二.原理

AB进程的处于虚拟地址空间共享区的空间,都通过页表映射到物理内存的同一块区域,这就保证了不同进程看到了同一份资源,进而实现了进程间通信。

注意:

1.开辟空间,建立映射等一系列操作,是由OS完成的,用户通过系统调用来间接实现以上操作。

2.内核设定上这个空间(step1)是允许共享的,不会因AB进程进行写时拷贝,导致改变其中一个进程的指向,进而无法通信。

3.通信完成后释放空间时,AB进程都是把申请的虚拟地址空间free掉,然后去掉页表上的映射关系,共享内存没有指针指向它(是否在使用共享内存),自然而然被OS释放掉,那么操作系统怎么知道有没指针指向共享内存呢?答案是引用计数!当引用计数减到0,就没有进程使用共享内存,即没有进程的指针指向共享内存,这时OS就会释放共享内存。

4.先描述,再组织

三.共享内存接口的使用

1.创建共享内存——shmget

形参解析

①.size

共享内存的大小

②.shmflg

标记位,一个宏,有两种:IPC_CREAT和IPC_EXCL

IPC_CREAT:创建一个共享内存,共享内存不存在,就先创建;如果存在,就打开这个已经存在共享内存,并返回。

IPC_EXCL:单用是滚木,毫无意义;需用“|”配合IPC_CREAT使用。即IPC_CREAT|IPC_EXCL。

共享内存不存在,就创建共享内存;如果存在,shmget出错返回!保证了shmget只要成功返回,就一定是一个新的共享内存!

③.key

在说这个参数之前,我们先引出两个问题:

1.我们如何去评估一个共享内存是否存在?

2.怎么保证要进行共享内存通信的两个进程,拿到的是同一个共享内存呢?

问题的答案,就是这个key!key可以标识共享内存的唯一性。既然有唯一性,就代表着其必然存在。这里需要注意一个点,key并非是内核生成的,而是在用户层生成,当生成以后,要手动传给shmget这个系统调用!

那为什么是在用户层生成呢?

进程A调用shmget接口,创建了共享内存空间,但是进程B怎么连接上这个空间呢?首先,让进程B再去调用shmget接口,是绝对行不通的,这只能构建新的共享内存空间。那么在系统层面,把key传给B进程,反正key能保证共享内存的唯一性,这不就成了吗?还是不行!因为在系统层面想把key从进程A传给B,是要基于双方已经建立通信的!

所以AB进程在建立通信之前,会在用户层约定一个key(在用户层约定是不用建立通信的),A进程把key传给系统中,对内存空间进行描述的结构体,作为识别共享内存空间的身份证;随后在用户层拿到key的进程B,就会根据这个key,找到对应的共享内存空间了。

那么如何约定的呢?这里用了另一种通信方式——命名管道!只要让AB打开同一路径下的同一管道文件,就可以实现key的传递。

注意:共享内存包括描述共享内存的struct+其指向的共享内存空间!

④.返回值

那么shmget创建成功以后,这个int返回值是啥?反正不会是key,key是给内核用的,用户不会去使用key,这是一个用户层的整数值(标识值)。在用户这,是通过这个返回值来标识共享内存的;后续用户在调用其他系统调用时,也是通过这个返回值,来访问共享内存。

2.生成key——ftok

理论上说,key可以随意生成,但是会带来冲突,所以我们一般使用一个接口(内含算法)——ftok,来为我们构建冲突较小的key。这玩意是个纯用户层面的算法。

pathname字符串和id都可以瞎写,但是建议pathname就写当前工作目录的路径"."。算法会将这俩货做混合,最终构建出我们的key。如果还是发生冲突,就更改两个形参,以保证key的唯一性。

构建key成功,就会返回key。

2.5.去关联——shmdt

在对共享内存进行删除之前,还需先将虚拟地址空间与共享内存去关联。

3.删除共享内存

进程结束,如果没有删除共享内存,其还会一直存在!!这跟文件不一样,文件的引用计数为0时,代表打开文件的所以进程全部退出,就会释放文件。所以共享内存的生命周期随内核!

①.删除共享内存的指令——ipcrm

ipcrm -m + shmid(即shmget的返回值,再说一遍,不是key!!!!)

那么怎么看到shmid呢?答案是指令ipcs -m:

注意:

指令是针对用户层面的封装,是运行于用户层面的!所以要用shmid管理运行内存!

②.删除共享内存的接口——shmctl

这是个共享内存控制函数,通过不同的控制命令(形参cmd),可以对共享内存完成不同操作,最后一个形参事关共享内存的属性,进程删除操作的时候可以置为空。

cmd分三种:


- IPC_STAT:获取共享内存信息,内核把数据写入 buf ,buf不能为NULL

- IPC_SET:修改共享内存的权限、属主;从buf读取配置,buf不能为NULL

-IPC_RMID:标记删除共享内存段,buf传 NULL 即可【最重要】

形参buf指向描述共享内存的结构体shimd_ds,该结构体包含共享内存许多属性:

创建共享内存的进程在shmget以后,会把key设置到ipc_perm结构体的_key上。获取共享内存的进程会拿着key去与shimd_ds的_key进行对比,如果相同,就可以知道访问的是同一个共享内存。

4.将共享内存空间映射到进程地址空间——shmat

返回值:返回起始的虚拟地址。进程可以通过该起始虚拟地址,进而访问到对应的共享内存空间。

如果单纯从接口名来看:at是attach的意思,就是将共享内存挂载到进程的地址空间中。

形参解析:

shmaddr:虚拟地址的使用情况,OS最清楚,用户不能盲目设置一个虚拟地址,所以使用接口时,就将参数设置为nullptr。

shmflg:既想对共享内存读,也想对其写,所以设为0,使用默认配置。

四.关于读写

在搭建信道的时候,用到了大量的系统调用,但是在对共享内存进行读写操作时,是没有用到系统调用的!是直接对共享内存相关联的虚拟地址进行操作的!

以上确实是有系统调用,但它属于搭建信道的范畴。

这里进行写操作的时候,将虚拟地址空间当成一个大的字符串,用for循环进行一个个字符的写入:

读取是直接获取虚拟地址空间的初始值,这时的虚拟地址空间,已经被写入了:

总结:

五.共享内存通信的特点和弊端

共享内存通信有个弊端,通信双方的进程,是没有同步机制这个保护机制的!一个写的进程压根没有生成,另一个读的进程生成以后,不会阻塞,等待写入,而是去不断地读取。这会导致数据不一致,比如A进程打算写aabb,结果写完aab,就立马被B进程读了去(AB是并发运行的,你读你的,我写我的)。

在AB进程间再创建一条命名管道,A进程会在写入共享内存的时候,倘若写完自己想要写的,就通过管道,向B发通知;B进程不准直接读共享内存,而是要等待管道的通知;通知到了,B进程才被唤醒,去共享内存读取。

六.共享内存的大小

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询