Linux_ pipe 匿名管道 浅解

管道
1 使用信号进行通信
进程之间使用“信号”进行通信的优缺点
优点:简单
缺点:传递的信息有限,
只能传递一个简单的“信号值”

解决方案:
使用“管道”进行进程间通信。

注: 进程间通信,简称“IPC”
2 什么是管道
IPC 有多种方式, 管道是IPC的最基本的方式.
管道是“半双工”的,即是单向的。
管道是FIFO(先进先出)的。

单进程中的管道:
int fd[2]
使用文件描述符fd[1], 向管道写数据
使用文件描述符fd[0], 从管道读数据

fd[0]
用户进程
fd[1]
用户进程

fd[1]
fd[0]

管道

内核

注:单进程中的管道无实际用处
管道用于多进程间通信。

3 管道的创建
使用pipe系统调用
用法:见 man 2 pipe

返回值:
成功:返回 0
失败:返回 -1

注意:获取两个“文件描述符”
分别对应管道的读端和写端。
fd[0]: 是管道的读端
fd[1]: 是管道的写端
如果对fd[0]进行写操作,对fd[1]进行读操作,可能导致不可预期的错误。

4 管道的使用
实例1:单进程使用管道进行通信
main1. c
注意:创建管道后,获得该管道的两个文件描述符,
不需要普通文件操作中的open操作
如图:

实例2:多进程使用管道进行通信
main2.c
注意:创建管道之后,再创建子进程,此时一共有4个文件描述符。
4个端口,父子进程分别有一个读端口和一个写端口
向任意一个写端口写数据,即可从任意一个读端口获取数据。
如图:

实例3:子进程使用exec启动新程序时管道的使用
main3.c

    有程序P1, P2
    使用管道进行通信
    P1由用户输入一个字符串,然后把该字符串发给p2
    P2接收到以后,把该字符串打印出来

P1:
创建管道
创建子进程
在子进程中用exec替换成p2,
(在使用exec 时,把管道的读端作为exec的参数)
在父进程中,获取用户的输入,然后把所输入的字符串发送给p2
(即,父进程把字符串写入管道)

P2:
从参数中获取管道的读端(参数即为p2的main函数的参数)
读管道
把读到的字符串打印出来

难点:子进程使用exec启动新程序运行后,
新进程能够使用原来子进程的管道(因为exec能共享原来的文件描述符)
但问题是新进程并不知道原来的文件描述符是多少!

解决方案:
把子进程中的管道文件描述符,用exec的参数传递给新进程。

实例4:关闭管道的读端/写端

管道关闭后的读操作:

问题:
对管道进行read时,如果管道中已经没有数据了,此时读操作将被“阻塞”。
如果此时管道的写端已经被close了,则写操作将可能被一直阻塞!
而此时的阻塞已经没有任何意义了。(因为管道的写端已经被关闭,即不会再写入数据了)

解决方案:
如果不准备再向管道写入数据,则把该管道的所有写端都关闭,
则,此时再对该管道read时,就会返回0,而不再阻塞该读操作。(管道的特性)
注意,这是管道的特性。
如果有多个写端口,而只关闭了一个写端,那么无数据时读操作仍将被阻塞。

实际实现方式:
父子进程各有一个管道的读端和写端;
把父进程的读端(或写端)关闭;
把子进程的写端(或读端)关闭;
使这个“4端口”管道变成单向的“2端口”管道,如图:

代码:main4.c
(改写main2.c)
把父进程的写操作注释掉,此时子进程的读操作将被一直阻塞
把父进程的写操作注释掉,并close父进程的写端
此时子进程的读操作将将被阻塞。
把父进程的写操作注释掉,并把父子进程的写端都close
此时子进程读操作将直接返回0,而不再阻塞。
最终实现方案:
关闭父进程的读端,关闭子进程的写端。
当父进程不再发送数据时,就关闭本进程的写端。

练习:创建一个子进程
父进程通过管道向子进程发送数据(字符串)
该字符串由用户输入。
当用户输入”exit”时, 就不再向子进程发送数据,并关闭该端的管道。

子进程从管道读取数据,并输出。
直到父进程关闭了管道的写端。

练习:main5.c
创建一个子进程
父进程:
循环等待用户输入,
用户每输入一个单词后,就把该单词用管道发送给子进程,
直到用户输入exit。
子进程:
每收到一个单词后,就打印输出
直到用户在父进程中结束输入。

实例5 把管道作为标准输入和标准输出
把管道作为标准输入和标准输出的优点:
子进程使用exec启动新程序时,就不需要再把管道的文件描述符传递给新程序了。
可以直接使用使用标准输入(或标准输出)的程序。
比如 od –c (统计字符个数,结果为八进制)

实现原理:
使用dup复制文件描述符
用exec启动新程序后,原进程中已打开的文件描述符仍保持打开,
即可以共享原进程中的文件描述符。

代码:main6.c (修改main3.c)
注意:dup的用法
dup复制文件描述符,
返回的新文件描述符和被复制的文件描述符,指向同一个文件或管道
5 使用popen/pclose
popen的作用:
用来在两个程序之间传递数据:
在程序A中使用popen调用程序B时,有两种用法:
程序A读取程序B的输出(使用fread读取)
程序A发送数据给程序B,以作为程序B的标准输入。(使用fwrite写入)

用法:man popen
返回值:成功,返回FILE*
失败, 返回空
实例1: 读取外部程序的输出
main7.c
实例2:把输出写到外部程序
main8.c
popen的原理:
先使用fork创建一个子进程,
然后在子进程中使用exec执行指定外部程序,并返回一个文件指针FILE*给父进程。
当使用”r”时,该FILE指向外部程序的标准输出
当使用”w”时,该FILE指向外部程序的标准输入。

popen的优缺点:
优点:可以使用shell扩展(比如命令中可以使用通配符)
使用方便。
缺点:每调用一次popen, 将要启动两个进程(shell和被指定的程序),
资源消耗大。

如果所有管道写端对应的文件描述符被关闭,则read返回0
如果所有管道读端对应的文件描述符被关闭,则write操作会产生信号SIGPIPE

原文地址:https://www.cnblogs.com/Sico2Sico/p/5384217.html