进程间通信(IPC)
- 1.常见的通信方式
- 2.低级IPC方法
- 文件
- 3.常用于本机的IPC机制
- 3.1无名管道pipe
- 3.2命名管道FIFO
- 3.3消息队列MessageQueue
- 3.4共享内存SharedMemory
- 3.5信号量Semaphore
- 3.6信号Signal
- 3.7unix域套接字
- 4.不同计算机上的IPC机制
- 5.IPC机制的数据拷贝次数
1.常见的通信方式
- 文件:进程间可以经由fork,exec以及文件系统传送文件。
- 管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
- 命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
- 消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
- 共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
- 信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
- 信号Signal : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
- unix域套接字:unix域套接字用于在同一台计算机上运行的进程之间的通信,其可以在两个进程间传送打开文件描述符。
- 套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。
2.低级IPC方法
文件
进程间通信可以通过传送打开文件(fork,exec与文件系统)来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法。但一般说来,进程间通信不包括这种似乎比较低级的通信方法。
3.常用于本机的IPC机制
3.1无名管道pipe
特点:
无名管道是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
因为没有名字,因此无名管道只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
无名管道只存在于内存中,其实质是一个内核缓冲区。进程以先进先出的方式从缓冲区存取数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据。
无名管道由pipe()函数创建:
#include <unistd.h>
int pipe(int filedis[2]);
参数filedis返回两个文件描述符:filedes[0]为读而打开,filedes[1]为写而打开。filedes[1]的输出是filedes[0]的输入。
3.2命名管道FIFO
命名管道和无名管道的主要区别在于,命名管道有一个文件名字,这个文件名对应于一个磁盘索引节点,有了这个文件名,任何进程有相应的权限都可以对它进行访问。
Linux中通过系统调用mknod()或makefifo()来创建一个命名管道。
mkfifo myfifo
mknod myfifo p
3.3消息队列MessageQueue
消息队列与管道通信相比,其优势在于接收的时候不需要按照队列次序,而是可以根据自定义条件接收特定类型的消息。
3.4共享内存SharedMemory
共享内存机制其允许两个或多个进程共享一个给定的存储区,这一段存储区可以被两个或两个以上的进程映射至自身的地址空间中,一个进程写入共享内存的信息,可以被其他使用这个共享内存的进程,通过一个简单的内存读取错做读出,从而实现了进程间的通信。
采用共享内存进行通信的一个主要好处是效率高,因为进程可以直接读写内存,而不需要任何数据的拷贝,对于像管道和消息队里等通信方式,则需要再内核和用户空间进行四次的数据拷贝,而共享内存则只拷贝两次:一次从输入文件到共享内存区,另一次从共享内存到输出文件。
3.5信号量Semaphore
信号量(semaphore)与已经介绍过的 IPC 结构不同,它是一个计数器。信号量用于实现进程间的互斥与同步,而不是用于存储进程间通信数据。
特点
- 信号量用于进程间同步,若要在进程间传递数据需要结合共享内存。
- 信号量基于操作系统的 PV 操作,程序对信号量的操作都是原子操作。
- 每次对信号量的PV 操作不仅限于对信号量值减1或加1,而且可以加减任意正整数。
P操作:信号量的值减1,若信号量的值减1后小于0,则该进程被阻塞后放入等待该信号量的等待队列中。
V操作:信号量的值加1,若信号量的值加1后的结果小于或等于0,则从该信号的等待队列中释放一个等待进程。
3.6信号Signal
内核进程利用信号来通知用户进程发生了哪些系统事件,用户进程对信号的响应方式如下:
- 忽略信号:对信号不做任何处理,但是有两个信号不能忽略:即SIGKILL及SIGSTOP。
- 捕捉信号:定义信号处理函数,当信号发生时,执行相应的处理函数。但是有两个信号不能捕获:即SIGKILL及SIGSTOP。
- 执行缺省操作:Linux对每种信号都规定了默认操作。
3.7unix域套接字
Unix域套接字用于同一台pc上运行的进程之间通信,它仅仅复制数据,不执行协议处理(比如增加删除网络报头,计算校验和,产生顺序号,发送确认报文),因此Unix域套接字往往比通信两端位于同一主机的TCP套接字要快。
4.不同计算机上的IPC机制
套接字Socket
套接口(socket)编程是实现不同计算机中进程间通信的主要方式之一。我们熟知的WWW服务、FTP服务、TELNET服务等都是基于套接口编程来实现的。除了在异地的计算机进程间以外,套接口同样适用于本地同一台计算机内部的进程间通信。
5.IPC机制的数据拷贝次数
共享内存是最高效的进程间通信的方式,因为把同一块物理内存的地址空间映射到不同进程的地址空间当中,那么不同的进程之间通信,通过直接修改地址空间当中的内存即可,该机制的实现只需要两次拷贝即可实现,即数据从用户空间到内存,数据再从内存到用户空间。
其它的进程通信机制需要四次拷贝操作。假设现在A和B间需要通信,首先从进程A的缓冲区中将数据拷贝到内核中,其次内核将数据拷贝到内存中,之后数据又从内存被拷贝到内核,最后内核将数据拷贝到进程B的缓冲区中。因此使用共享内存通信比较高效。