一、管道的概念
当从一个进程连接数据流到另一个进程时,我们使用管道。通常把一个进程的输出通过管道连接到另一个进程的输入。
管道可以用来在两个进程之间传递数据,如: ps -ef | grep “bash”, 其中‘|’就是管道,其作用就是将ps命令的结果写入管道文件,然后grep再从管道文件中读出该数据进行过滤。
用一个普通文件也可以达到进程间通信,但是效率太低了。普通文件存放在磁盘或者硬盘,性能比较低。管道类型的文件是存放在内存中的,关机之后管道在内存中分配的空间也就没有了,只在进程临时通信的时候使用,管道文件的打开必须至少有一个进程在读文件并且有一个进程在写文件。并且对于管道文件必须是以只读或只写的方式打开。
进程间的通信方式有3种:单工、半双工和全双工。
管道的通信方式是半双工。
二、有名管道
有名管道可以在任意两个进程之间通信 。创建的管道文件其实是在内存中分配了一块空间,向管道中写入的数据实际上写入了内存,所以管道文件的大小永远为0。
有名管道的创建:
mkfifo FIFO//创建一个名为FIFO的管道文件
1. 创建一个管道文件fifo
创建一个有名管道fifo,read.c文件负责向管道文件fifo中读取数据,write.c文件负责向管道文件fifo中写入数据。
2. 打开管道文件fifo
(1)如果对管道文件进行只读操作,或者只写操作,进程就会被阻塞,必须读和写同时进行:
write.c文件的代码如下:
read.c文件的代码如下:
编译并运行write和read程序,结果如下:
由结果可以看出:
只执行read文件向管道文件读数据时,进程发生阻塞,这个阻塞是在read.c文件中open打开管道文件的位置发生的。同理,只执行write文件向管道文件写入数据时,进程也会发生阻塞,这个阻塞同样也是在write.c文件中open打开管道文件的位置发生的。
(2)以读和写操作同时打开管道文件fifo
打开两个终端,两个终端分别执行write和read程序,此时就可以成功打开管道文件fifo:
3. 向管道文件中写入数据并读取数据
(1)向管道文件fifo中写入一次数据并读取
write.c文件的代码如下:
read.c文件的代码如下:
打开两个终端,两个终端分别执行write和read程序,此时就可以成功打开管道文件fifo:
此时read程序执行到了read的位置被阻塞住,在等待读数据,因为管道文件fifo中现在没数据。而write程序执行到了fgets的位置在等待从键盘输入数据。
接下来write程序向管道文件fifo中写入数据,read程序就可以读到写入管道文件fifo中的数据:
(2)可以连续向管道文件fifo中写入数据并读取数据:
对于管道文件来说,如果写入管道文件的程序关闭了,那么读管道文件的程序就会返回0,解除阻塞;如果读管道文件的程序关闭了,那么写入管道文件的程序就会产生异常(SIGPIPE)。
write.c文件的代码:
read.c文件的代码如下:
打开两个终端,两个终端分别执行write和read程序,此时就可以成功打开管道文件fifo,可以连续向管道文件中写入数据并且读取数据:
三、无名管道
无名管道主要应用于父子进程间的通信。 父进程可以通过管道进行数据的读取和写入,子进程也可以通过管道进行数据的读取和写入。如果父进程向管道中写入数据,子进程向管道中读取数据,那么父进程就把读端关闭,子进程就把写端关闭;相反父进程向管道中读取数据,子进程向管道中写入数据,那么父进程就把写端关闭,子进程就把读端关闭。这样才能形成一个单向的数据流。
无名管道的创建是通过pipe来创建:
其中:
pipe()如果成功返回0,失败则返回-1;
fds[0]是管道读端的描述符
fds[1]是管道写端的描述符
无名管道没有名字,它只能通过将文件描述符复制给子进程,然后让父子进程进行通信。
1. 无名管道的创建与应用
创建一个无名管道,父进程父进程向管道中写入数据,子进程向管道中读取数据。
代码如下:
【注意】先要将管道创建成功,这样才会有文件描述符。
运行结果:
四、总结
1.无论有名还是无名,写入管道的数据都在内存中
2.管道是一种半双工通信方式(通信方式有单工、半双工、全双工)
3.有名和无名管道的区别是有名管道可以在任意进程间使用,而无名管道主要在父子进程间通信。