一,什么是共享内存
共享内存是进程间通信的一种方式,相较于传统的管道和命名文件的通信方式,shared memory是最快的一种方式,但是他也有一定的缺陷,下面再谈。
共享内存区是最快的IPC形式。一旦这样的内存映射到共享它的进程的地址空间,这些进程间数据传递不再涉及到 内核,换句话说是进程不再通过执行进入内核的系统调用来传递彼此的数据。
二,共享内存通信的原理
我们要想实现不同的进程之间相互通信,第一步就一定要先让不同的进程看到同一份资源,在使用共享内存的时候,存在于内核内存中的共享内存就是这份资源。
第一步:
创建共享内存
要让不同的进程看到同一份资源的前提是,我们要先让这份资源存在。可以通过调用系统调用接口开创建共享内存。
接口函数我们先不介绍,原理讲完之后,在用代码实现的时候,我们会对原理进行复盘,到时候具体细节再说。
现在假设共享内存我们已经在内存中开辟好了,如下图所示:
第二步:
让多个进程(这里我们就以两个进程为例来谈具体的细节) 与这个共享内存建立链接,也就是让共享内存的物理地址映射到该进程的进程地址空间上去。具体一点说是,映射到进程地址空间的共享区。如下图所示:
第三步:
上述建立完链接之后,两个进程就能看到同一份资源了,各自通过对应的地址就能够完成向共享内存中写入或读取数据了。这个过程就是双方进行通信的过程,在下述的代码中会有更好的体现。
第四步:
断开链接+释放共享内存
断开链接和释放共享内存,是两个步骤,将它们放在一起合成第四点就是为了体现一点,释放共享内存之前一定要先让链接该空间的进程与其断连。
三,代码
代码的讲解大部分使用注释的方式,这样更能让大家清晰的看到创建共享内存的整个过程。
一,创建共享内存
要创建共享内存的话,我们先来认识一下shmget()函数。
shmget有三个参数,先讲第二个参数size:表示的是要创建的共享内存的大小。
第三个参数有多个选项,但是我们只需要认识下面两个就行:
1.单独使用IPC_CREAT: 创建一个共享内存,如果共享内存不存在,就创建之,如果已经存在,获取已经存在的共享内存并返回
2.IPC_EXCL不能单独使用,一般都要配合IPC_CREAT
3.IPC_CREAT | IPC_EXCL: 创建一个共享内存,如果共享内存不存在,就创建之, 如果已经存在,则立马出错返回 -- 如果创建成功,对应的shm,一定是最新的!
返回值:
成功返回的是共享内存的标识符
失败-1被返回。
第一个参数我们需要单独使用一个函数来获取。
ftok()的 两个参数分别是项目的路径名和项目的id,如何使用在代码中有体现,这里解释一下,为什么要使用这个函数来获取第一个参数。
如果共享内存创建成功之后,该共享内存的标识符就会被返回,但是在OS中存在着许多的共享内存,我们需要区分去该共享内存是我们创建的还是OS的,所以就需要一个方式来创建一个唯一的共享内存,ftok函数会根据你给出的项目名和项目id来为你生成一个整数,该整数具有唯一性 ,根据这个整数就能够创建一个唯一的共享内存使用,从而保证不会与其他的共享内存冲突使用。
了解完了之后,我们就开始实现第一步的代码。
创建两个文件,代表两个进程,让其中一个进程进行写,另一个进行读取。
server.cc进行读取
client.cc进行写入操作。
二,建立链接
建立链接时,我们任然需要借助一个函数-shmat
其中第一个参数就是我们使用shmget()的返回值,也就是共享内存的标识。
第二个和第三个参数并不需要了解的太深入,感兴趣的话可以查一下网上的很多文章,这里我们直接将这两个参数分别设置成nulllptr和0即可。
该函数的返回值就是共享内存的起始地址。
代码:
三,开始通信
通信的控制就比较的自由了,这里就让client向共享内存中写一些数据,如何让server去读取数据并打印出来。
代码如下:
编译完成之后,我们让server先运行起来,创建一个共享内存,如何再运行起client,观察结果:
可以很好的看到,在我们的没有运行cilent的时候,server一直在等待,当我们运行起client的时候,共享内存中被写入数据,server读取了该数据并进行了打印。
这时一切都是正常的,可是当我们删除可执行文件之后,再重新编译,想运行可执行文件的时候,就会发生下面的报错信息:
原因就是我们上面所说的,我们在完成通信之后并没有将两个进程与共享内存进行去关联,也没有删除这个共享内存,所以我们就看到了报错:这个文件已经存在。
解法1:使用命令查看该共享内存的shmid,根据这个shmid进行删除:
还可以使用函数进行删除。该函数就是shmctl()
第二个和第三个参数这里任然不过多的介绍,分别设置为IPC_RMID和nullptr 。
四,去关联
在删除的时候,因为我们是server创建的该共享内存,所以我们只需要让server进行删除即可,在删除之前让两个进程进行去关联。
去关联要使用的函数是shmdt()
它的使用也很简单,参数就是共享内存的起始地址,也就是shmat()的返回值。
代码如下:
接下来再进行一下测试,看一下是否还会存在上述出现的问题:
我们让server进程过6秒之后,自动的退出,退出之后再查看一下共享内存是否被自动删除。
所以我们将代码进行一些修改:
实验结果:
可以看到再进程server退出的时候,共享内存也被自动删除了。
总结:
在搞懂共享内存通信的原理之后,我们主要就是需要掌握一些相关函数的使用,使用共享内存进行通信我们发现也不是很难,只不过函数相对于其他的通信方式有点小多。所以认识这些函数和熟练的使用就我们需要做大量的练习。
源代码:
共享内存博客素材 · 0495797 · wxk/mylinux - Gitee.com