IPC——共享内存

概述

管道是OS在物理内存上开辟一段缓存空间,当进程通过read、write等API来共享读写这段空间时,就实现了进程间通信。

消息队列是OS创建的链表,链表的所有节点都是保存在物理内存上的,所以消息队列这个链表其实也是OS在物理内存上所开辟的缓存,当进程调用msgsnd、msgrcv等API来共享读写时,就实现了进程间通信。

共享内存也逃不开同样的套路。共享内存就是OS在物理内存中开辟一大段缓存空间,不过与管道、消息队列调用read、write、msgsnd、msgrcv等API来读写所不同的是,使用共享内存通信时,进程是直接使用地址来共享读写的。

当然不管使用那种方式,只要能够共享操作同一段缓存,就都可以实现进程间的通信。

 

信号、管道、消息队列、共享内存对比

信号:非精确通信

管道:无名管道只用于亲缘进程,命名管道克服了这一缺点。但是这两种管道方式还是不适合网状通信

消息队列:克服了无名管道只用于亲缘进程,无名/命名管道 网状通信若的缺点。但是不能实现大规模数据的通信。

共享内存:继承了消息队列的优点,还克服了其缺点。支持大规模数据通信。

为啥共享内存比消息队列效率高?

前面这4中IPC,其本质都是操作OS提供的一段虚拟内存(在引入虚拟内存机制的情况下),虚拟内存最终还是被OS映射到真实物理内存。信号、管道、消息队列 都要调用各种API,在到达内存之前,经过了多次函数调用,直到最后一个函数,该函数才会通过地址去读写共享的缓存。层层调用势必会严重降低效率。而共享内存就没有这么多麻烦,直接使用地址来读写患处,效率高,那是必须的!

 

共享内存原理

每个进程的虚拟内存只严格对应自己的那片物理内存空间,也就是说虚拟空间的虚拟地址,只和自己的那片物理内存空间的物理地址建立映射关系,和其它进程的物理内存空间没有任何的交集,因此进程空间之间是完全独立的。

以两个进程使用共享内存来通信为例,实现的方法就是:

(1)调用API,让OS在物理内存上开辟出一大段缓存空间。
(2)让各自进程空间与开辟出的缓存空间建立映射关系

建立映射关系后,每个进程都可以通过映射后的虚拟地址来共享操作实现通信了。

多个进程能不能映射到同一片空间,然后数据共享呢?

当然是可以的。不过当多个进程映射并共享同一个空间时,在写数据的时候可能会出现相互干扰,比如A进程的数据刚写了一半没写完,结果切换到B进程后,B进程又开始写,A的数据就被中间B的数据给岔开了。这时往往需要加保护措施,让每个进程在没有操作时不要被别人干扰,等操作完以后,别的进程才能写数据。

 

共享内存的使用步骤

①进程调用shmget函数创建新的或获取已有共享内存。shm是share memory的缩写。

②进程调用shmat函数,将物理内存映射到自己的进程空间。即让虚拟地址和真实物理地址建议一一对应的映射关系。建立映射后,就可以直接使用虚拟地址来读写共享的内存空间了。

③shmdt函数,取消映射

④调用shmctl函数释放开辟的那片物理内存空间和消息队列的msgctl的功能是一样的,只不过这个是共享内存的。

多个进程使用共享内存通信时,创建者只需要一个,同样的,一般都是谁先运行谁创建,其它后运行的进程发现已经被创建好了,就直接获取共享使用,大家共享操作同一个内存,即可实现通信。

 

API

shmget

函数原型

功能

参数

返回值

shmat

函数原型

功能

参数

返回值

shmdt

函数原型

功能

参数

返回值

shmctl

函数原型

功能

参数

返回值

猜你喜欢

转载自www.cnblogs.com/kelamoyujuzhen/p/9392802.html