我们都知道操作系统下的进程不止一个,且它们都是相互独立的。但是大多数时候为了完成一个任务,往往需要进程间相互协作,一个进程的某些结果都需要传给其它进程,如数据传输、资源共享等等。那么这些进程会通过哪些渠道进行沟通交流呢?
linux下主要有3种进程间通信方式:管道;System V进程间通信;POSIX进程间通信
今天主要讲解一下第一种进程间通信方式(IPC):管道
一听到管道这个词,相信很多人都会想到下水管道、天然气管道等等。那么Linux下的管道有何不同呢? 管道本质上是内核中的一块缓冲区,因为Linux下一切皆文件,所以操作系统是通过文件操作的形式来操作管道的。 管道主要分为两种:匿名管道和命名管道
匿名管道,顾名思义,是没有名字的管道。它不可见于文件系统,仅能用于具有亲缘关系的进程间通信。
int pipe(int fd[2]); //创建匿名管道
匿名管道如果创建成功的话会返回2个文件描述符供我们对管道进行操作:fd[0] 、fd[1] 。其中fd[0]表示读端、fd[1]表示写端。如果不好记的话此处告诉你一个好记的方法:0长的是不是像嘴巴?那肯定就是读端啦。1像一根笔,肯定就是写端啦!!!
前边说到匿名管道只能用于具有亲缘关系的进程间通信。那么具体是如何实现的呢?
其实就是父进程创建了子进程,而子进程不是以父进程为模板,复制了父进程的PCB么,肯定也复制了父进程的文件描述符表啦。因此子进程也会有两个文件描述符,而且指向同一个管道。此时因为父子进程都能访问到这个管道,那么它们便可以进行通信了。但是由于管道是半双工单向通信,即数据只能单向传递,所以在进行通信前得确定好数据的流向:如果只读就关闭写端,如果只写就关闭读端。
此外,因为管道是在进程中创建的,当所有操作该管道的进程都退出时,管道也就被释放了。所以,管道的生命周期随进程。
另外,我们知道,一个文件同一时间只能被一个进程操控,即进行读写操作。如果同一时间有两个进程打开文件进行操控的话,肯定会破坏文件。所以,为了文件资源的安全性,同一时间只能有一个进程访问。 值得注意的是,如果此时管道中没有任何数据的话,读操作将不能进行,直到管道中被写入数据;如果管道中已经被写满了的话,写操作将不能进行,直到有数据被读走。所以,匿名管道自带同步与互斥,其中同步指的是访问的可控时序性,互斥指的是对资源同一时间的唯一访问性。
所以,匿名管道的特性有以下几点:
1.只能用于具有亲缘关系的进程间通信
2.半双工单向通信
3.生命周期随进程
4.面向字节流
5.自带同步与互斥
命名管道:可见于文件系统,是一个管道类型的文件
跟匿名管道不同的是,命名管道可以应用于同一主机上的任意进程间通信!!! 那么它是如何创建的呢?
mkfifo filename //命令创建
int mkfifo(const char *pathname,mode_t mode); //代码创建
命名管道的特性和匿名管道相同!!!
命名管道打开规则:
1.如果以只读方式打开命名管道,那么open函数将阻塞等待,直到有其它进程以写方式打开这个命名管道;
2.如果以只写方式打开命名管道,那么open函数将阻塞等待,直到有其它进程以读方式打开这个命名管道;
3.如果以读写方式打开,则不会阻塞
总结一下管道的特性:
1.匿名管道不可见于文件系统,只能用于具有亲缘关系的进程间通信;命名管道可见于文件系统,是一个特殊类型(管道类型)的文件,可用于同一主机上的任意进程间通信。
2.管道是一个半双工通信方式,只能单向通信。
3.提供面向字节流数据传输服务(传输的数据无规则无边界,收发灵活)
4.管道的生命周期随进程
5.管道自带同步与互斥(同步:临界资源访问的可控时序性;互斥:对临界资源同一时间的唯一访问性)
管道的读写特性:
1.如果管道中没有数据,读取操作会阻塞(描述符的默认属性为阻塞属性)。如果描述符被设置为非阻塞属性,那么读取操作不会被阻塞,而是直接报错返回。
2.如果管道中数据满了,写入操作会阻塞(描述符的默认属性为阻塞属性)。如果描述符被设置为非阻塞属性,那么写入操作也不会被阻塞,而是直接报错返回。
3.如果管道的写端全部关闭,那么read读取数据的时候会返回0。
4.如果管道的读端全部关闭,那么write写入数据的时候会触发异常,操作系统会发送SIGPIPE信号到进程,导致进程退出。
5.当写入的数据大小超过PIPE_BUF,则这个操作是一个原子操作,有可能被打断。