进程间七种通信方法,IPC的各种应用场景和优缺点
进程间的7种通信方式
传统的通信方式:
有名管道
无名管道
信号
IPC通信:
消息队列
共享内存
信号量
BSD:
socket
【1】无名管道
定义:
无名管道是一种特殊类型的文件,在内核空间中对应的资源即是一段内存空间,内核在这段空间以循环对列的方式临时存入一个进程发送给另一个进程的信息,这段内核空间完全由操作系统管理和维护,应用程序只需要,也只能通过系统调用来访它。
无名管道和普通的文件有很大的差异:无名管道的内核资源在通信两进程退出后会自动释放。跟普通文件不同。
存储大量常规信息,但是编程方式,具有和普通文件一样的特点,可以使用read/write等函数进行读写操作,只是
注意:特殊的文件只能用文件IO操作。
读写的特点有一定的差异,另外,不能用lseek函数来修改当前的读写位置,因为FIFO需要满足FIFO的原则。
特点:
1、使用条件:只能用于具有亲缘关系(父子进程,兄弟进程等)的进程之间的通信
2、通信模式:半双工模式,fd[0]作为读端,fd[1]作为写端
3、读写方式:对于它的读写采用文件IO(不支持lseek函数)
4、读操作会阻塞(等待):在管道中无数据情况下。
写操作会阻塞(等待):当管道被写满时,无名管道的大小为64K
5、管道破裂:管道读端关闭,再向管道中写数据时。
即向管道中写入数据的进程将收到内核传来的SIGPIPE信号。
【2】有名管道
定义:
有名管道:有自己的名字,但是有名管道名称保存在磁盘上,但是内容保存在内核中
有名管道和普通的文件一样具有磁盘存放路径,文件的权限和其他的属性信息,但是有名管道和普通文件又有区别,有名管道没有在磁盘上存真正的信息,而是在内存中存放,2个进程结束后自动丢失,通信结束后有名管道的文件路径本身存在,这是和无名管道区别的地方。
特点:
1、有名管道可以使互不相关的两个进程互相通信。
2、有名管道可以通过路径名来指出,并且在文件系统中可见。
3、读写方式:对于它的读写采用文件IO(不支持lseek函数)
4、其它与无名管道一样
5.有名管道读端写端不固定。一段读,另一端写。当然某一端既可以读也可以写,要用父子进程实现。
【3】信号---进程间唯一的一种异步通信方式。
【4】IPC对象
消息队列
共享内存
信号量
这三种都是IPC通信方式
【4】IPC对象
system V IPC对象
和文件一样,IPC在使用前必须创建,每种IPC有特定的生产者,所有者和访问权限
使用ipcs查看
手动删除某个ipc机制,使用ipcrm命令
linux系统为每个ipc机制都分配了唯一的id,所有针对该ipc机制的操作都使用该id值,那么通信双方
需要通过某个办法获取ID值,创建者根据创建函数的返回值可以获取该值,linux2个进程不能随意访问对方的空间
,也就不能直接获取这一ID值。
解决办法:ipc在实现时约定使用可以值作为参数创建,如果在创建时使用了相同的值将可以得到一个IPC对象的ID
(即一方创建,另一方获取的是ID)
ftok---获取key值
#include <sys/types.h>
#include <sys/ipc.h>
key_t ftok(const char *pathname, int proj_id);
函数的功能:生成外部键值用于2个不相关的进程建立联系
参数:pathname 路径名,必须传一个已经存在的 一般传入当前路径"."
proj_id 整型变量 字符 ASCII
返回值:成功返回key(键值)
失败-1
注意:如果使用相同的文件路径及整数,得到的key值是唯一的,唯一的key值创建某类IPC机制时将得到同一个IPC
机制(但是使用使用相同的key值分别创建一个消息队列和一个信号,两者没有关系),而文件的路径的
访问对两个进程来说很容易统一,因此便捷地实现了2个进程间通信机制ID的确定
(一)、消息队列
1. 消息队列是消息的链表,存放在内存中,由内核维护
2. 特点
1)消息队列允许一个或多个进程向它写入或者读取消息,并且每条消息都有类型
2)消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,编程时可以按消息的类型读取。
3)与无名管道、有名管道一样,从消息队列中读出消息,消息队列中数据会被删除。
4)消息队列中的消息是有格式的。
5)只有内核重启或人工删除时,该消息才会被删除,若不人工删除消息队列,消息队列会一直存在于内存中
6)消息队列标识符,来标识消息队列。消息队列在整个系统中是唯一的。
(二)共享内存
共享内存是System V版本的最后一个进程间通信方式。共享内存,顾名思义就是允许两个不相关的进程访问同一个逻辑内存,共享内存是两个正在运行的进程之间共享和传递数据的一种非常有效的方式。不同进程之间共享的内存通常为同一段物理内存。进程可以将同一段物理内存连接到他们自己的地址空间中,所有的进程都可以访问共享内存中的地址。如果某个进程向共享内存写入数据,所做的改动将立即影响到可以访问同一段共享内存的任何其他进程。
特别提醒:共享内存并未提供同步机制,也就是说,在第一个进程结束对共享内存的写操作之前,并无自动机制可以阻止第二个进程开始对它进行读取,所以我们通常需要用其他的机制来同步对共享内存的访问,例如信号量。
下面就 Shared Memory 的IPC作以阐述与分析。
共享内存的通信原理
在Linux中,每个进程都有属于自己的进程控制块(PCB)和地址空间(Addr Space),并且都有一个与之对应的页表,负责将进程的虚拟地址与物理地址进行映射,通过内存管理单元(MMU)进行管理。两个不同的虚拟地址通过页表映射到物理空间的同一区域,它们所指向的这块区域即共享内存。
共享内存的通信原理示意图:
对于上图我的理解是:当两个进程通过页表将虚拟地址映射到物理地址时,在物理地址中有一块共同的内存区,即共享内存,这块内存可以被两个进程同时看到。这样当一个进程进行写操作,另一个进程读操作就可以实现进程间通信。但是,我们要确保一个进程在写的时候不能被读,因此我们使用信号量来实现同步与互斥。
对于一个共享内存,实现采用的是引用计数的原理,当进程脱离共享存储区后,计数器减一,挂架成功时,计数器加一,只有当计数器变为零时,才能被删除。当进程终止时,它所附加的共享存储区都会自动脱离。
为什么共享内存速度最快?
借助上图说明:Proc A 进程给内存中写数据, Proc B 进程从内存中读取数据,在此期间一共发生了两次复制
(1)Proc A 到共享内存 (2)共享内存到 Proc B
因为直接在内存上操作,所以共享内存的速度也就提高了。
(三)信号量
信号量主要是用来保护共享资源,使得资源在一个时刻只有一个进程(线程)所拥有。
进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。
信号量有以下两种类型:
二值信号量: 最简单的信号量形式,信号量的值只能取0或1,类似于互斥锁。 注:二值信号量能够实现互斥锁的功能,但两者的关注内容不同。信号量强调共享资源,只要共享资源可用,其他进程同样可以修改信号量的值;互斥锁更强调进程,占用资源的进程使用完资源后,必须由进程本身来解锁。
计算信号量:信号量的值可以取任意非负值(当然受内核本身的约束)。
三。socket通信
Socket是面向”客户/服务器“(C/S)模型而设计的,针对客户和服务器程序提供不同的Socket系统调用。这种模式巧妙地解决了进程之间建立通信连接的问题。服务器Socket会公告给需要通信的一方。
不妨考虑一下,两个完全随机的用户进程之间如何建立通信?假如通信双方都不知道对方的socket编号,就好比打电话的双方彼此不知道对方的电话号码,要通话是不可能的。采用了C/S模式后,由client端发起请求,server端接收请求,并应答,从而建立好链接。