进程间通信方式(IPC)
进程间通信(Inter process communication,简称IPC)指的是进程之间的信息交换,进程间通信的方式有很多,比如管道通信、信号通信、共享内存、消息队列、信号量组、POSIX信号量等。
进程间通信可以达到数据传输、共享资源、控制进程等目的,方便用户对进程进行控制和管理。
当然,Linux系统中这么多种进程间的通信方式并不是一同出现的,而是在不同时期被设计出来,Linux系统属于类Unix系统,所以Linux系统继承了很多Unix系统的设计,比如Unix系统的System-V版本中就引入了三种进程间通信方式,分别是消息队列、共享内存、信号量组。这三种通信方式也被称为System-V IPC对象。
共享内存的概念
-
共享内存是Linux系统进程间通信的一种方式,是在Unix系统的system-V版本引入的一种IPC对象,除了共享内存外,其他的IPC对象还包含消息队列、信号量组。
-
共享内存其实就是指多个进程可以共享物理内存中的同一段内存区域,只不过还需要把物理内存映射到进程的私有虚拟地址空间中,这样多个进程就可以对同一块内存进行访问,效率非常高。
共享内存的通信原理示意图
- 对于上图我的理解是:当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥。
*- 对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂架成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。
为什么共享内存的通信速率是三种IPC通信方式中最快的
- 借上图说明Proc A进程将数据写入共享内存中,Proc B进程从共享内存中直接读取数据,这个过程中一共只发生了两次数据操作。因为直接对内存进行操作所以通信方式的速度得到了提升。
共享内存的创建
Linux系统中提供了一个名称叫做shmget()的函数接口,利用该接口可以向内核申请物理内存
-
[参数key]:由ftok生成的key标识,标识系统的唯一IPC资源。(用户一般用ftok生成一个唯一的键值)
-
[参数size]:需要申请共享内存的大小。在操作系统中,申请内存的最小单位为页,一页是4k字节,为了避免内存碎片,我们一般申请的内存大小为页的整数倍。(宏定义PAGE_SIZE的倍数)
-
[参数shmflg]:如果要创建新的共享内存,需要使用IPC_CREAT,IPC_EXCL,如果是已经存在的,可以使用IPC_CREAT或直接传0。
-
[返回值]:成功时返回一个新建或已经存在的的共享内存标识符,取决于shmflg的参数。失败返回-1并设置错误码。
-
[注意事项]:可以看到,申请成功的共享内存段里面存储的内容会被自动初始化为0,并且内核会为每一块创建的共享内存分配一个shmid_ds结构体来记录共享内存的属性和信息。
共享内存的挂接与去关联
挂接
Linux系统中提供了一个名为shmat()的函数接口,用户利用该接口可以实现将物理的共享内存映射到进程的虚拟内存中。
-
[参数shmid]:共享存储段的标识符。
-
[参数*shmaddr]:shmaddr = 0,则存储段连接到由内核选择的第一个可以地址上(推荐使用)。
-
[参数shmflg]:若指定了SHM_RDONLY位,则以只读方式连接此段,否则以读写方式连接此段。
-
[返回值]:成功返回共享存储段的指针(虚拟地址),并且内核将使其与该共享存储段相关的shmid_ds结构中的shm_nattch计数器加1(类似于引用计数);出错返回-1。
去关联
同时上图中也提到了一个名为shmdt()的函数接口,可实现将shmat链接的物理内存与虚拟内存之间的关联断掉,使物理内存脱离当前的进程。
-
[参数*shmaddr]:连接以后返回的地址。
-
[返回值]:成功返回0,并将shmid_ds结构体中的 shm_nattch计数器减1;出错返回-1。
共享内存的控制
Linux系统中提供了一个名为shmctl()的函数接口,用户可以通过调用该函数接口实现设置共享内存的属性、获取共享内存的属性、删除共享内存等系列操作
-
[参数shmid]:共享存储段标识符。
-
[参数cmd]:指定的执行操作,设置为IPC_RMID时表示可以删除共享内存。
-
[参数*buf]:设置为NULL即可。
-
[返回值]:成功返回0,失败返回-1
总结
-
优点:我们可以看到使用共享内存进行进程之间的通信是非常方便的,而且函数的接口也比较简单,数据的共享还使进程间的数据不用传送,而是直接访问内存,加快了程序的效率。
-
缺点:共享内存没有提供同步机制,这使得我们在使用共享内存进行进程之间的通信时,往往需要借助其他手段来保证进程之间的同步工作。(锁/信号量)