用于 Windows 的 CMS/TSO 管道 - 第二部分:数据流
IBM 管道(Windows 版)和 PowerShell 的实现。
引言
CMS/TSO PipeLines 是在 IBM 大型机上运行的产品。在 z/VM 系统上,它在 CMS 下运行。在 z/OS 系统上,它在 TSO 下运行。顾名思义,数据通过一系列内置或用户定义的阶段进行传递。在此过程中,数据可以被修改或从管道中删除。您可能会想,这没什么新意。不同之处在于 PipeLines 支持并发并行管道,并且数据可以被路由进出管道。
系统要求
虽然 PipeLib
类库可以从 .NET 程序访问,但我认为在 PowerShell 下运行它会更有益。我所有的测试都是通过 PowerShell 进行的,我提供的任何管道很可能都是 PowerShell 脚本。因此,我建议的最低系统要求如下:
- PowerShell V1.0
- .NET Framework V2
数据流
数据可以被看作是按顺序从一个阶段流向另一个阶段。当数据被发送到第二个管道时,也是如此。它在第二个管道中按顺序流动。但是,当第二个管道将数据路由回第一个管道时会发生什么?在第一个管道中合并后的数据序列是什么?
看似矛盾的陈述
管道阶段的运行顺序是未定义的。 您可能会问,数据流的概念如何与前一段兼容?答案是,阶段中的 IO 逻辑对数据流施加了顺序。一个阶段使用以下逻辑处理数据:
- 当数据可用时,执行以下操作:
- 预览数据缓冲区中下一个可用记录
- 处理该记录
- 写入记录
- 等待记录被读取阶段消耗
- 读取,从数据缓冲区中消耗记录
- 从步骤 1 重复
只要一个阶段遵循这个基本逻辑,数据就会按顺序流经管道。
示例
'(end ?) < file.txt'
'|f:find abc'+
'|spec /-> / 1 1-* n'+
'|m:faninany'+
'|console'+
'? f:'+
'|spec / / 1 1-* n'+
'|m:'
此管道读取一个文件,并查找以字符串 "abc
" 开头的记录。找到后,它会被标记并写入控制台。如果未找到该字符串,则记录文本会向右移位,并被发送回第一个管道以写入控制台。当 find 阶段写入记录时,无论是写入其主输出还是次输出,它都会等待记录被消耗后再读取下一条记录。spec 阶段获取记录,写入其输出,然后等待。faninary 阶段获取并写入记录,然后等待。console 阶段获取记录,将其写入终端,由于它是最后一个阶段,且没有写入对象,因此它会消耗该记录。
当 console 阶段消耗了记录,就会释放 faninany 阶段的等待状态,然后它会消耗记录。然后是 spec 阶段,然后又回到 find 阶段。find 阶段读取下一条记录,然后过程重新开始。
如果未强制执行这种读取、写入和等待的结构,写入控制台的数据将与数据文件中的数据顺序不符。faninany 阶段从任何有数据的输入读取数据。因此,在没有强制结构的情况下,它可能会从其主流读取几条记录,然后再读取其次流,从而改变数据顺序。
管道卡死
当数据停止流动时,管道就卡死了。考虑以下情况:
'(end ?) ...'+
'|o:fanout'+
'|...'+
'|i:fanin'+
'|...'+
'? o:'+
'|...'+
'i:'
fanout 阶段将其记录写入其主输出。fanin 阶段读取它,然后对其主输入发出读取下一条记录的请求。然后 fanout 阶段将其记录写入其次输出。此记录最终到达 fanin 阶段的次输入。然后管道卡死。为什么?fanin 阶段正在等待其主输入上的数据,而 fanout 阶段正在等待其次输出上的记录被消耗。您会发生锁定,导致管道卡死。一般来说,所有的卡死都是由一个写入阶段等待一个读取阶段,而读取阶段又等待该写入阶段引起的。
防止卡死
为了防止卡死,您需要一个消耗其输入的阶段,从而在写入其输出之前释放写入阶段。使用上面的例子:
'(end ?) ...'+
'|o:fanout'+
'|...'+
'|i:fanin'+
'|...'+
'? o:'+
'|...'+
'|buffer'+
'i:'
buffer 阶段在写入其内部缓冲区的内容之前读取并存储所有输入。现在,当 fanout 阶段在其输入上收到 EOF 条件时,它会将该条件转发到其主输出和次输出。当 fanin 阶段在其主输入上收到 EOF 时,它开始从其次输入读取。buffer 阶段在其主输入上收到 EOF,并开始写入其缓冲区的 contents。正在等待数据的 fanin 阶段现在可以从缓冲区读取数据。
使用 faninany 阶段而不是 fanin 也可以防止卡死。这可能会改变输出记录的顺序,您可能需要添加其他阶段来保留顺序(如果这是要求的话)。
当管道卡死时,您会看到类似如下的消息:
WARNING: PipeLine stalled: stages still running are:
P1S1-< State: WaitingWrite
P1S2-fanout State: WaitingWrite
P1S3-fanin State: WaitingRead
P1S4-cons State: WaitingRead
生成此消息的管道是:
'(end ?) < sss.ps1'+
'|o:fanout'+
'|i:fanin'+
'|cons'+
'? o:|i:'
如果管道卡死,插入一个 buffer 阶段几乎肯定可以解决问题。但是,取决于管道,buffer 阶段可能有点大材小用。buffer 阶段缓冲所有输入,直到 EOF 才写入。也许 copy 阶段就足够了。它在写入之前缓冲一条记录。或者,elastic 阶段。它缓冲足够多的记录以防止卡死。
摘要
本文的目的是解释数据如何通过管道流动,卡死是如何产生的以及如何防止卡死。
敬请期待第三部分:用户编写的阶段。