进程间通信
概念:进程间通信是多个进程间进行数据交互。
但是,操作系统是如何能够让多个进程进行通信?
因为进程之间具有独立性,进程与进程相互沟通配合就不大方便。要想完成进程间通信,就需要有一个公共的资源让多个进程都能访问到。
注意:进程间通信需要操作系统提供各种方法主要是因为进程的独立性。
目的:
- 数据传输
- 资源共享
- 进程控制
- 通知事件
进程间通信的几种方式:
借鉴unix而来的方式:
- 管道(掌握)
system v标准的方式:
- 消息队列(了解)
- 共享内存(掌握)
- 信号量(了解)
POSIX标准的方式:(在线程时进行说明)
- 消息队列
- 共享内存
- 信号量
- 互斥量
- 条件变量
- 读写锁
管道
概念:用于传输资源,其本质是操作系统提供的一块缓冲区。
当作文件使用时,是内核中的一段内存,可以理解为内核中的队列(先进先出)。
根据unix“一切皆文件”的思想,管道就是通过文件描述符进行操作,即基本IO操作。
匿名管道
因为创建缓冲区没有任何的标记,操作系统只是返回了文件描述符提供的进程操作,对于其他的进程来说就访问不到这一个缓冲区了。因此匿名管道只能通过子进程拷贝父进程PCB这种方式获取到相同的描述符,而对相同的管道进行操作,实现进程间通信。所以,匿名管道只适用于具有亲缘关系的进程间通信。
读写特性:
- 阻塞:为了完成功能发起一个调用,如果不具备完成条件,则一直等待。
- 写满不写,读完不读,即管道空,读数据阻塞,管道满,写数据阻塞。
- 同步:对临界资源访问的时序可控性。
- 互斥:保证临界资源同一时间的唯一访问性。
- 临界资源:大家都能访问到的资源。
- 如果管道的写端全部关闭,读完管道中的数据后,read的时候会返回0。
- 如果管道的读端全部关闭,write的时候会触发异常(会导致程序退出)。
管道特点:
- 只能用于具有共同祖先的进程(具有亲缘关系的进程)之间进行通信;通常,一个管道由一个进程创建,然后该进程调用fork,此后父子进程之间就可以应用该进程。
- 管道提供流式服务,即面向字节流,读写不必严格匹配,但是有可能造成数据粘连
- 进程退出,管道释放,所以管道的生命周期随进程
- 内核会对管道操作进行同步与互斥,写满了就不写了,读完了就不读了
- 管道是半双工的,即数据是单向流动,需要双方通信时,需要建立起两个管道
int pipe(int fd[2]) //参数:文件描述符数组,fd[0]表示读端,fd[1]表示写端 //返回值:成功返回0,失败返回错误代码
pipe函数调用成功,就会返回一对文件描述符,故到fd数组中,本质上是创建了一个匿名管道,匿名管道其实是内核中的一段内存,在加上Linux中一切皆文件的思想,就通过这一对文件描述符操作管道对应的内存。
命名管道
概念:也是一块缓冲区,可见于文件系统,因此可以用于同一台机器上的任意进程间通信。
命名管道是一种特殊类型的文件。
数据报传输:传输一整条一整条的数据,虽然不灵活,但是不会造成数据粘连。
匿名管道与命名管道的区别:
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,需要用户自己打开。
- FIFO与pipe之间唯一的区别在于他们的创建于打开方式不同,一但这些工作完成后,他们具有相同的语义。
管道特性:
- 匿名管道具备的特性,命名管道都具备,除了这些命名管道还具备打开特性。
- 如果以只读方式打开,会阻塞在open这里,等待有其他进程以写的方式打开。
- 如果以只写方式打开,会阻塞在open这里,等待有其他进程以读的方式打开。
- 如果以读写方式打开,就不会阻塞了。