Как устроено в юниксе - не знаю. Но мне кажется, что можно считать корректным следующий подход. Для начала - схема, как это работает в юниксе на уровне пользователя.## Код:
```
rawdata -> p1 -> stream1 -> p2 -> stream2 -> p3 -> stream3 -> data
```

-> stream ->
stream - участок потока, влекущий данные между двумя точками их обработки. Стрелка слева символизирует вливание в поток данных, обработанных на предыдущей точке, а справа - загрузка данных на очередную точку.

-> p ->
p - точка, на которой происходит обработка данных.
Стрелка слева символизирует получение данных из предыдущего участка потока, а стрелка справа - возвращение данных в поток на следующий его участок.

Если на конвейере точек обработки не будет, то данные на выходе окажутся такими же, как и на входе.

Как организовать подачу данных на вход и приём их на выходе - отдельная задача, которая решается однозначно не так, как в юниксе: у нас не терминал а ББ. Поэтому решайте её любым удобным для Вас способом.

А подача данных внутри решается довольно просто.
Интерфейс точки обработки данных должен быть таким:
## Код:
```
p = RECORD
  prev : StreamSection;
  process : ...;
  next : StreamSection
END
```

Точка читает из участка потока данных сколько ей надо, и записывает обработанные данные в следующий участок.

Активным элементом точки должна быть команда ББ так? Она будет обрабатывать данные, пока в предыдущем участке не закончились данные или пока в следующем не закончилось место. Если команда закончила работу, то переходим к команде из следующей точки и т.д.

Ну а интерфейс конвейера такой:
## Код:
```
pipe = RECORD
  Add(x : p);
  Start()
END
```
(Сюда не включены процедуры загрузки исходных данных и получения итоговых данных)
Команда Add добавляет вместе с точкой обработки ещё и участок конвейера (автоматически).

P.S. Надеюсь, я понятно описал, а то так поздно, что я уже не уверен, что изъясняюсь внятно.