管道——(1)有名管道和无名管道

2 篇文章 0 订阅
1 篇文章 0 订阅

一、引子

之前,两个进程间要发送消息,是通过信号来完成的。创建通知事件,通过它引起响应,但是传送的信息只是一个信号值而已。

如果两个进程需要交换更有用、复杂的信息,就需要用到——管道。

管道——一个进程链接数据流到另一个进程,简单理解就是把一个进程的输出直接传递给另一个进程的输入。

为了更好地理解“管道”,我们举例:现有两个进程a、b,a进程中fgets“hello”,b进程printf“hello”(a

进程中的),该如何实现呢?

首先,我们可能想到的一种方法就是将a进程中的“hello”通过open、write调用写入到一个文件中,再让进程b读取这个文件中的内容,输出到屏幕上就可以了。

但是,文件是存储在磁盘上的,读写速度非常慢,读写效率也低,不宜采用。管道文件的数据存放在内存中,读写效率高,更适合用在“两个进程间的通信”上。



二、有名管道

1.概念:有名管道,通常被称为“FIFO文件”或“命名管道”,通常用于不相关的进程间的通信(任意两个进程间的通信),它是一种特殊的文件,在文件系统中以文件名的形式存在,可以在命令行上用“mkfifo fifo”创建一个名为“fifo”的有名管道。当然,有一些版本的Unix系统里面,还可以使用“mknod filename p”来创建一个有名管道文件,推荐使用前面的。


2.写端和读端:一个管道文件,需要两个进程,一个读称为“读端”,一个写称为“写端”。




3.使用有名管道的例子:

第一步:先创建一个有名管道文件——fifo,两个.c文件——write.c和read.c(分别对这个管道文件进行“读”和“写”)。

第二步:在write.c中,以“只读”的形式打开有名管道文件“fifo”,打印打开fifo文件得到的文件描述符——fw,然后不断地将从标准输入——stdin中的内容放入数组——buff中,将buff中的内容write写入与文件描述符fw有关的文件“fifo”中,并且当buff中的前三个字符为“end”时,停止从标准输入中读取内容,直接关闭写端。



第三步:在read.c中,以“只读”的形式打开管道文件“fifo”,打印得到的文件描述符——fr,然后将从文件“fifo”中读取的内容放入数组——buff中,再打印到标准输出中,如果读“fifo”时再没有什么数据可读(即写端没有再往文件“fifo”中写入内容),则打印“对方(写端)已关闭”,再跳出关闭读端。


第四步:检验

1.


分别在两个终端运行write和read,首先两边打印了各自得到的文件描述符。

同时,我们观察到:两个程序都没有再继续执行了,为什么呢?

回答是:阻塞住了。这时写端还没有开始写入,读端自然要等待它写入之后再读出。

2.


我们在写端不断地输入内容,读端同时也作出反应,将我们写入的内容都打印了出来。

3.


最后,我们再来验证一下,两者退出循环的条件。

写端输入了“end”字眼,跳出循环,结束写端程序。读端程序处理完“end bye”之后,继续等待写端输入,可是这是写端已经关闭了,所以读端什么也读不到(n==0),这才打印“对方已经关闭”,最后读端进程结束。


总结:

1.管道有读端和写端之分,当写端关闭之后,读端read就会返回0,并且随后也关闭。

2.阻塞:当读端没有数据可读时,read调用通常会阻塞,即它将暂停进程(读端进程)来等待直到有数据到达为止。但是,这种阻塞没有多大用处,所以read调用就会返回0,写端程序中的“if(n==0)及之后的语句”都是来应对这种阻塞的。

问题:当写端关闭后,读端因为迟迟读不到数据,随后才结束,那么如果读端先结束呢?写端该有什么反应?

这时,写端写入数据就会发生异常,继而产生信号——SIGPIPE(13),来结束自己,但是这种异常是可以控制的,所以要避免“先关闭读端进程”这种情况。



我们在写端增添以上的部分,当读端直接关闭时,写端写完数据之后就会发送这个信号给自己,来终止进程。

以下是演示结果:

最后打印的是信号——SIGPIPE的信号值。


三、无名管道

1.概念:顾名思义,无名管道就是没有“名字”的管道,它不是以文件的形式存在于文件系统中,我们是无法直接找到它的,因此无名管道只适用于“父子进程间的通信”。我们用系统调用“int pipe(int fd[2])”来创建无名管道,fd[2]中存放的是两个文件描述符,其中fd[0]默认负责读端,而fd[1]负责写端。

2.父子进程间的通信——无名管道:



父子进程间的通信”可以通过无名管道来完成,在父子进程中都使用系统调用“pipe(fd[2])”来创建一个无名管道,但是在父进程中关闭fd[0](读端),让父进程负责“写端”,在子进程中关闭fd[1](写端),让子进程负责“读端”。

3.具体实现:

第一部分:我们先在一个进程中实现一个无名管道。


我们在这同一个进程中,使用系统调用pipe(fd[2]),写端和读端同时开着,先向写端里输入“hello”,休眠1秒,再从读端中读出“hello”,最后的结果:


第二部分:父子进程间的无名管道



在这个程序中,创建了一个子进程,所以这实际上是两个进程之间的通信,我们让子进程负责读端,让父进程负责写端。

fork调用使得子进程的fd[2]内容与父进程的fd[2]一样,所以父子进程享用的是同一个无名管道

在父进程的写端中写入标准输入中输入的内容,子进程中的读端则将管道中的内容读出到标准输出。

结果见下图:



以上就是有名管道和无名管道的相关内容,管道的其他相关内容将在以后推出。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值