进程间七种通信⽅法,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端接收请求,并应答,从⽽建⽴好链接。