管道过滤器(Pipe-And-Filter)模式
按照《POSA(面向模式的软件架构)》里的说法,管道过滤器(Pipe-And-Filter)应该属于架构模式,因为它通常决定了一个系统的基 本架构。管道过滤器和生产流水线类似,在生产流水线上,原材料在流水线上经一道一道的工序,最后形成某种有用的产品。在管道过滤器中,数据经过一个一个的 过滤器,最后得到需要的数据。一、基本的管道过滤器:
管道负责数据的传递,它把原始数据传递给第一个过滤器,把一个过滤器的输出传递给下一个过滤器,作为下一个过滤器的输入,重复这个过程直到处理结 束。要注意的是,管道只是对数据传输的抽象,它可能是管道,也可能是其它通信方式,甚至什么都没有(所有过滤器都在原始数据基础上进行处理)。
过滤器负责数据的处理,过滤器可以有多个,每个过滤器对数据做特定的处理,它们之间没有依赖关系,一个过滤器不必知道其它过滤器的存在。这种松耦合 的设计,使得过滤器只需要实现单一的功能,从而降低了系统的复杂度,也使得过滤器之间依赖最小,从而以更加灵活的组合来实现新的功能。
编译器就是基于管道过滤器模式设计的:
输入:源程序
预处理:负责宏展开和去掉注释等工作。
编译:进行词法分析、语法分析、语义分析、代码优化和代码产生。
汇编:负责把汇编代码转换成机器指令,生成目标文件。
链接:负责把多个目标文件、静态库和共享库链接成可执行文件/共享库。
输出:可执行文件/共享库。
二、复合过滤器
过滤器可以由多个其它过滤器组合起来的,比如上面的“编译”过程可以认为是一个复合过滤器:
输入:预处理之后的源代码。
词法分析:负责将源程序分解成一个一个的token,这些token是组成源程序的基本单元。
语法分析:把词法分析得到的token解析成语法树。
语义分析:对语法树进行类型检查等语义分析。
代码优化:对语法树进行重组和修改,以优化代码的速度和大小。
代码产生:根据语法树产生汇编代码。
输出:汇编代码。
三、支持多个输入的过滤器
过滤器可以有多个输入。比如上面“链接”,它接收多个输入:
链接”过滤器能接收多个数据源,如目标文件、静态库和共享库。
四、具有多个输出的过滤器
过滤器可以有多个输出。如多媒体播放器的解码过程:
输入:AVI文件,包括音频和视频数据。
分离器:把音频和视频数据分离成两个流,音频数据传递给音频解码器,视频数据传递给视频解码器。
音频解码器:把压缩的音频数据解码成原始的音频数据。
视频解码器:把压缩的视频数据解码成原始的图像数据。
输出:音频数据传递给声卡,图像数据传递给显示器。
管道过滤器是最贴近程序员生活的模式,也是Unix-like系统的基本设计理念之一。作为Linux下的程序员,我们天天都使用这个模式。
linux shell中管道发挥的作用是文件描述符重定向,例如 prog1 | prog2 | prog3,管道会将prog1的标准输出重定向为prog2的标准输入,将prog2的标准输出重定向为prog3的标准输入,prog1的标准输入和 prog3的标准输出并没有改变。比如命令"ps -ef | grep -w "nginx""将ps命令的标准输出内容作为grep的输入,两个命令的组合的只输出关于nginx进程的信息。
这里归档两个平时没有想明白的问题:
1、shell管道中程序按什么顺序执行?进程关系是什么样子?
当前有很多的shell程序,实现各不同,有的支持作业控制,代表有:bash(bourne again shell),有的不支持,代表有bourne shell。
prog1 | prog2 | prog3
1)在不支持作业控制shell中,prog3是shell的子进程,而prog1和prog2为prog3的子进程。执行如下图所示:
2)对于支持作业控制的shell,prog1、prog2、prog3都为shell的子进程,在bash中的执行顺序为 prog1、prog2、prog3,具体和shell的实现有关。
2、如何判断管道中程序是否成功执行?某个程序执行失败是否影响其它程序的执行?
1)shell中有一个变量数组PIPESTATUS,保存了上一个执行管道的状态。
echo ${PIPESTATUS[*]};就可以输出所有管道进程的执行状态。
[root@zhangst test]# ./0 | ./1 | ./2
[root@zhangst test]# echo ${PIPESTATUS[*]}
0 1 2
0、1、2三个程序main函数中只包含一条return代码,分别返回 0,1,2。
2)bash中管道程序的执行相互不影响的,参考下面的例子:
[root@zhangst test]# ./0 | grep -v | ./2
用法: grep [选项]... PATTERN [FILE]...
试用‘grep --help’来获得更多信息。
2
0
[root@zhangst test]# echo ${PIPESTATUS[*]}
0 2 2
0、2三个程序分别在标准错误输出输出0、2,然后返回0、2
grep -v是不正确的,不能正确执行,但是0和2都正确执行完毕了。