| Page: | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
|---|
The STREAMS Programmer's Guide describes everything you need to know about the STREAMS toolset so that you can develop UNIX operating system communication services.
This guide contains chapters regarding the following components of the STREAMS interface:
This book occasionally refers to other books, notably the reference manuals:
These books contain the manual pages for the various commands, system calls, library functions, file contents, and devices. Within each book, manual pages are grouped numerically by section numbers. Within a section, the pages are sorted alphabetically, without regard to the letter that follows the section number. For example, the manual pages for Sections 3C, 3E, 3I, 3M, 3N, 3S, 3W, and 3X are all sorted together within Chapter 3 in the SUPER-UX Programmer's Reference Manual.
Manual pages are referred to with the function name showing first in constant width font, followed by the section number appearing in parenthesis in normal font. For example, the Executable and Linking Format Library (ELF) manual page appears as elf(3E). Reference manuals will not be referred to individually; however, individual sections are referred to as "Section 3E in the Reference Manuals."
The SUPER-UX Programmer's Reference Manual, SUPER-UX System Administrator's Reference Manual, and SUPER-UX User's Reference Manual are foundation documents which describe formally and comprehensively every feature of the UNIX system, and are a recommended supplement to this book.
| Home |
|---|
1.1.2 Notation Conventions
The following conventions are observed in this book:
$ cc file.c file.c file.c
The dollar sign is the default system prompt for the ordinary user. There is an implied RETURN at the end of each command. When a command extends beyond the width of the page, the break is marked with a backslash and an indented second line:
$ cc -L../archives -L../mylibs
file1.c file2.c file3.c \
file4.c -lfoo
Of course, a command that extends beyond the width of your terminal screen will wrap around. You
should use the backslash only if you enter the command exactly as we show it.
| SUPER-UX's STREAMS does not support single thread. Please refer to MT usage. |
In code examples, an #ifdef MT style convention is used as shown:
#ifdef ST
code for ST usage.
#endif
#ifdef MT
other code for MT usage.
#else
other code for ST usage.
#endif
In general text, usage-specific information is flagged as shown:
Information relevant only in MT usage.
Information relevant only in ST usage.
There are many statements in this book where the difference between the ST and MT versions is only a
function name and, typically, the function names are almost identical. Using the previously shown convention in
these cases would make the book awkward to read; consequently, a second style is here defined for such
"small'' differences. The following symbol is used to represent both ST-version and MT-version function names.
The set of these symbols and their appearance follows:

Thus, the sentence
should be read as
for ST, and as
for MT.
| Home |
|---|
Read the following for STREAMS configuration notes.
STREAMS is a general, flexible facility and a set of tools for development of UNIX system communication services. It supports the implementation of services ranging from complete networking protocol suites to individual device drivers. STREAMS defines standard interfaces for character input/output within the kernel, and between the kernel and the rest of the UNIX system. The associated mechanism is simple and open-ended. It consists of a set of system calls, kernel resources, and kernel routines.
The standard interface and mechanism enable modular, portable development and easy integration of high-performance network services and their components. STREAMS does not impose any specific network architecture. The STREAMS user interface is upwardly compatible with the character I/O user level functions such as open, close, read, write, and ioctl.
A Stream is a full-duplex processing and data transfer path between a STREAMS driver in kernel space and a process in user space (see Figure 1-1). In the kernel, a Stream is constructed by linking a Stream head, a driver, and zero or more modules between the Stream head and driver. The Stream head is the end of the Stream nearest to the user process. All system calls made by a user level process on a Stream are processed by the Stream head.
A STREAMS driver may be a device driver that provides the services of an external I/O device, or a software driver, commonly referred to as a pseudo-device driver. The driver typically handles data transfer between the kernel and the device and does little or no processing of data other than conversion between data structures used by the STREAMS mechanism and data structures that the device understands.
A STREAMS module represents processing functions to be performed on data flowing on the Stream. The module is a defined set of kernel-level routines and data structures used to process data, status, and control information. Data processing may involve changing the way the data is represented, adding or deleting header and trailer information to data, and/or packetizing and depacketizing data. Status and control information includes signals and input/output control information. Each module is self-contained and functionally isolated from any other component in the Stream except its two neighboring components. The module communicates with its neighbors by passing messages. The module is not a required component in STREAMS, whereas the driver is required.

Figure 1-1 Simple Streams
One or more modules may be inserted into a Stream between the Stream head and driver to perform intermediate processing of messages as they pass between the Stream head and driver. STREAMS modules are dynamically interconnected in a Stream by a user process. No kernel programming, assembly, or link editing is required to create the interconnection.
STREAMS uses queue structures to keep information about given instances of a pushed module or opened STREAMS device. A queue is a data structure that contains status information, a pointer to routines for processing messages, and pointers for administering the Stream. Queues are always allocated in pairs; one queue for the read-side and the other for the write-side. There is one queue pair for each driver and module, and the Stream head. The pair of queues is allocated whenever the Stream is opened or the module is pushed (added) onto the Stream.
Data is passed between a driver and the Stream head and between modules in the form of messages. A message is a set of data structures used to pass data, status, and control information between user processes, modules, and drivers. Messages that are passed from the Stream head toward the driver or from the process to the device, are said to travel downstream (also called write-side). Similarly, messages passed in the other direction, from the device to the process or from the driver to the Stream head, travel upstream (also called read-side).
A STREAMS message is made up of one or more message blocks. Each message block consists of a header, a data block, and a data buffer. The Stream head transfers data between the data space of a user process and STREAMS kernel data space. Data to be sent to a driver from a user process is packaged into STREAMS messages and passed downstream. When a message containing data arrives at the Stream head from downstream, the message is processed by the Stream head, which copies the data into user buffers.
Within a Stream, messages are distinguished by a type indicator. Certain message types sent upstream may cause the Stream head to perform specific actions, such as sending a signal to a user process. Other message types are intended to carry information within a Stream and are not directly seen by a user process.
| Home |
|---|
This section describes the basic set of operations for manipulating STREAMS entities.
A STREAMS driver is similar to a traditional character I/O driver in that it has one or more nodes associated with it in the file system, and it is accessed using the open system call. Typically, each file system node corresponds to a separate minor device for that driver. Opening different minor devices of a driver causes separate Streams to be connected between a user process and the driver. The file descriptor returned by the open call is used for further access to the Stream. If the same minor device is opened more than once, only one Stream is created; the first open call creates the Stream, and subsequent open calls return a file descriptor that references that Stream. Each process that opens the same minor device shares the same Stream to the device driver.
Once a device is opened, a user process can send data to the device using the write system call and receive data from the device using the read system call. Access to STREAMS drivers using read and write is compatible with the traditional character I/O mechanism.
The close system call closes a device and dismantles the associated Stream when the last open reference to the Stream is given up.
The following example shows how a simple Stream is used. In the example, the user program interacts with a communications device that provides point-to-point data transfer between two computers. Data written to the device transmitted over the communications line, and data arriving on the line can be retrieved by reading from the device.
#include <fcntl.h>
main( )
{
char buf[1024];
int fd, count;
if ((fd = open("/dev/comm/01", O_RDWR)) < 0) {
perror("open failed");
exit(1);
}
while ((count = read(fd, buf, 1024)) > 0) {
if (write(fd, buf, count) != count) {
perror("write failed");
break;
}
}
exit(0);
}
In the example, /dev/comm/01 identifies a minor device of the communications device driver. When this file is opened, the system recognizes the device as a STREAMS device and connects a Stream to the driver. Figure 1-2 shows the state of the Stream following the call to open.

Figure 1-2 Stream Communication Driver
This example illustrates a user reading data from the communications device and then writing the input back out to the same device. In short, this program echoes all input back over the communications line. The example assumes that a user sends data from the other side of the communications line. The program reads up to 1024 bytes at a time, and then writes the number of bytes just read.
The read call returns the available data, which may contain fewer than 1024 bytes. If no data is currently available at the Stream head, the read call blocks until data arrives.
Similarly, the write call attempts to send count bytes to /dev/comm/01. However, STREAMS implements a flow control mechanism that prevents a user from exhausting system resources by flooding a device driver with data.
Flow control controls the rate of message transfers among the modules, drivers, Stream head, and processes. Flow control is local to each Stream and advisory (voluntary). It limits the number of characters that can be queued for processing at any queue in a Stream, and limits buffers and related processing at any queue and in any one Stream, but does not consider buffer pool levels or buffer usage in other Streams. Flow control is not applied to high-priority messages.
If the Stream exerts flow control on the user, then the write call blocks it until flow control is relieved. The call does not return until it has sent count bytes to the device, exit, which is called to terminate the user process. It also closes all open files, thereby dismantling the Stream in this example.
This section gives an overview of the STREAMS components and discusses how these components interact with each other. A more detailed description of each STREAMS component is given later.
A queue is an interface between a STREAMS driver or module and the rest of the Stream. Queues are always allocated as an adjacent pair. The queue with the lower address in the pair is a read queue, and the queue with the higher address is used for the write queue.
A queue's service routine is invoked to process messages on the queue. It usually removes successive messages from the queue, processes them, and calls the put routine of the next module in the Stream to give the processed message to the next queue.
A queue's put routine is invoked by the preceding queue's put and/or service routine to add a message to the current queue. If a module does not need to enqueue messages, its put routine can call the neighboring queue's put routine.
Each queue also has a pointer to an open and close routine. The open routine of a driver is called when the driver is first opened and on every successive open of the Stream. The close routine of the driver is called when the last reference to the Stream is given up and the Stream is dismantled. The open routine of a module is called when the module is first pushed on the Stream and on every successive open of the Stream. The close routine of the module is called when the module is popped (removed) off the Stream.
| Home |
|---|
All input and output under STREAMS is based on messages. The objects passed between STREAMS modules are pointers to messages. All STREAMS messages use two data structures (msgb and datab) to refer to the message data. These data structures describe the type of the message and contain pointers to the data of the message, as well as other information. Messages are sent through a Stream by successive calls to the put procedure of each module or driver in the Stream.
All STREAMS messages are assigned message types to indicate their intended use by modules and drivers and to determine their handling by the Stream head. A driver or module can assign most types to a message it generates, and a module can modify a message type during processing. The Stream head converts certain system calls to specified message types and sends them downstream. It responds to other calls by copying the contents of certain message types that were sent upstream.
Most message types are internal to STREAMS and can only be passed from one STREAMS component to another. A few message types, for example M_DATA, M_PROTO, and M_PCPROTO, can also be passed between a Stream and user processes. M_DATA messages carry data within a Stream and between a Stream and a user process. M_PROTO or M_PCPROTO messages carry both data and control information.
Figure 1-3 shows that a STREAMS message consists of one or more linked message blocks that are attached to the first message block of the same message.

Figure 1-3 A Message
Messages can stand alone, as in Figure 1-3, when the message is being processed by a procedure. Alternately, a message can await processing on a linked list of messages, called a message queue. In Figure 1-4, Message 2 is linked to Message 1.

Figure 1-4 Messages on a Message Queue
When a message is on a queue, the first block of the message contains links to preceding and succeeding messages on the same message queue, in addition to the link to the second block of the message (if present). The message queue head and tail are contained in the queue.
STREAMS utility routines enable developers to manipulate messages and message queues.
In certain cases, messages containing urgent information (such as a break or alarm conditions) must pass through the Stream quickly. To accommodate these cases, STREAMS provides multiple classes of message queuing priority. All messages have an associated priority field. Normal (ordinary) messages have a priority of zero. Priority messages have a priority greater than zero. High-priority messages are high-priority by virtue of their message type. The priority field in high-priority messages is unused and should always be set to zero. STREAMS prevents high priority messages from being blocked by flow control and causes a service procedure to process them ahead of all ordinary messages on the queue. This results in the high priority message transiting each module with minimal delay.
Nonpriority, ordinary messages are placed at the end of the queue following all other messages in the queue. Priority messages can be either high priority or priority band messages. High-priority messages are placed at the head of the queue, but after any other high-priority messages already in the queue. Priority band messages that enable support of urgent, expedited data are placed in the queue after high-priority messages but before ordinary messages.
Message priority is defined by the message type; once a message is created, its priority cannot be changed. Certain message types come in equivalent high priority/ordinary pairs (for example, M_PCPROTO and M_PROTO), so that a module or device driver can choose between the two priorities when sending information.
A module performs intermediate transformations on messages passing between a Stream head and a driver. There may be zero or more modules in a Stream (zero when the driver performs all the required character and device processing).
Each module is constructed from a pair of queue structures (see Au/Ad and Bu/Bd in Figure 1-5). One queue performs functions on messages passing upstream through the module (Au and Bu). The other set (Ad and Bd) performs another set of functions on downstream messages.
Each queue in a module generally has distinct functions, that is, unrelated processing procedures and data. The queues operate independently and Au will not know if a message passes through Ad unless Ad is programmed to inform it. Messages and data can be shared only if the developer specifically programs the module functions to perform the sharing.
Each queue connects to the adjacent queue in the direction of message flow (for example, Au to Bu or Bd to Ad). In addition, within a module, a queue can readily locate its mate and access its messages and data.

Figure 1-5 Detailed Stream
| Home |
|---|
Each queue in a module points to messages, processing procedures, and data as follows:
In general, each queue in a module has a distinct set of all these elements.
| Home |
|---|
STREAMS device drivers are an initial part of a Stream. They are structurally similar to STREAMS modules. The call interfaces to driver routines are identical to the interfaces used for modules.
Three significant differences exist between modules and drivers. A driver must be able to handle interrupts from the device, a driver can have multiple Streams connected to it, and a driver is initialized/deinitialized using open and close, whereas a module is initialized/deinitialized using I_PUSHioctl and I_POPioctl.
Drivers and modules can pass signals, error codes, and return values to processes using message types provided for that purpose.
Earlier, Streams were described as linear connections of modules, where each invocation of a module is connected to at most one upstream module and one downstream module. While this configuration is suitable for many applications, others require the ability to multiplex Streams in a variety of configurations. Typical examples are terminal window facilities, and internetworking protocols (which might route data over several subnetworks).
Figure 1-6 shows an example of a multiplexer that multiplexes data from several upper Streams over a single lower Stream. An upper Stream is one that is upstream from a multiplexer, and a lower Stream is one that is downstream from a multiplexer. A terminal windowing facility might be implemented in this fashion, where each upper Stream is associated with a separate window.

Figure 1-7 shows a second type of multiplexer that might route data from a single upper Stream to one of several lower Streams. An internetworking protocol could take this form, where each lower Stream links the protocol to a different physical network.

Figure 1-7 One-to-Many Multiplexer
Figure 1-8 shows a third type of multiplexer that might route data from one of many upper Streams to one of many lower Streams.

Figure 1-8 Many-to-Many Multiplexer
| Home |
|---|
The STREAMS mechanism supports the multiplexing of Streams through special pseudo-device drivers. Using a linking facility, users can dynamically build, maintain, and dismantle multiplexed Stream configurations. Simple configurations like the ones shown in Figure 1-6 and Figure 1-8 can be further combined to form complex, multilevel multiplexed Stream configurations.
STREAMS multiplexing configurations are created in the kernel by interconnecting multiple Streams. Conceptually, there are two kinds of multiplexers: upper and lower. Lower multiplexers have multiple lower Streams between device drivers and the multiplexer, lower and upper multiplexers have multiple upper Streams between user processes and the multiplexer.
Figure 1-9 is an example of the multiplexer configuration that typically occurs where internetworking functions are included in the system. This configuration contains three hardware device drivers. The IP (Internet Protocol) is a multiplexer.
The IP multiplexer switches messages among the streams or sends them upstream to user processes in the system. In this example, the multiplexer expects to see the same interface downstream to Module 1, Module 2, and Driver 3.
| Home |
|---|

Figure 1-9 Internet Multiplexing Stream
Figure 1-9 depicts the IP multiplexer as part of a larger configuration. The multiplexer configuration, shown in the dashed rectangle, generally has an upper multiplexer and additional modules. Multiplexers can also be cascaded below the IP multiplexer driver if the device drivers are replaced by multiplexer drivers.
Figure 1-10 shows a multiplexer configuration where the multiplexer (or multiplexing driver) routes messages between the lower Stream and one upper Stream. This Stream performs X.25 multiplexing to multiple independent Switched Virtual Circuit (SVC) and Permanent Virtual Circuit (PVC) user processes. Upper multiplexers are a specific application of standard STREAMS facilities that support multiple minor devices in a device driver. This figure also shows that more complex configurations can be built by having one or more multiplexed drivers below and multiple modules above an upper multiplexer.
Developers can choose either upper or lower multiplexing, or both, when designing their applications. For example, a window multiplexer would have a similar configuration to the X.25 configuration of Figure 1-10, with a window driver replacing the Packet Layer, a tty driver replacing the driver XYZ, and the child processes of the terminal process replacing the user processes. Although the X.25 and window multiplexing Streams have similar configurations, their multiplexer drivers would differ significantly. The IP multiplexer in Figure 1-9 has a different configuration from the X.25 multiplexer, and the driver would implement its own set of processing and routing requirements in each configuration.
| Home |
|---|

Figure 1-10 X.25 Multiplexing Stream
In addition to upper and lower multiplexers, you can create more complex configurations by connecting STREAMS containing multiplexers to other multiplexer drivers. With such a diversity of needs for multiplexers, it is not possible to provide general-purpose multiplexer drivers. Instead, STREAMS provides a general purpose multiplexing facility, which allows users to set up the intermodule/driver plumbing to create multiplexer configurations of generally unlimited interconnection.
STREAMS provides the following benefits:
STREAMS also greatly simplifies the user interface for languages that have complex input and output requirements.
| Home |
|---|
STREAMS simplifies the creation of modules that present a service interface to any neighboring application program, module, or device driver. A service interface is defined at the boundary between two neighbors. In STREAMS, a service interface is a specified set of messages and the rules that allow passage of these messages across the boundary. A module that implements a service interface receives a message from a neighbor and responds with an appropriate action (for example, sends back a request to retransmit) based on the specific message received and the preceding sequence of messages.
In general, any two modules can be connected anywhere in a Stream. However, rational sequences are generally constructed by connecting modules with compatible protocol service interfaces. For example, a module that implements an X.25 protocol layer presents a protocol service interface at its input and output sides (seeFigure 1-11). In this example, other modules should only be connected to the input and output side if they have the compatible X.25 service interface.
STREAMS provides the capabilities to manipulate modules from the user level, to interchange modules with common service interfaces, and to change the service interface to a STREAMS user process. These capabilities yield further benefits when implementing networking services and protocols, including:
The following examples show the benefits of STREAMS capabilities for creating service interfaces and manipulating modules. These examples are only illustrations and do not necessarily reflect real situations.
Figure 1-11 shows how the same X.25 protocol module can be used with different drivers on different machines by implementing compatible service interfaces. The X.25 protocol module interfaces are Connection Oriented Network Service (CONS) and Link Access Protocol - Balanced (LAPB).

Figure 1-11 X.25 Multiplexing Stream
Alternate protocol modules (and device drivers) can be interchanged on the same machine if they are implemented to an equivalent service interface.
| Home |
|---|
Figure 1-12 illustrates how STREAMS can move functions between kernel software and front-end firmware. A common downstream service interface allows the transport protocol module to be independent of the number or type of modules below. The same transport module connects without change to either an X.25 module or X.25 driver that has the same service interface.
By shifting functions between software and firmware, developers can produce cost effective, functionally equivalent systems over a wide range of configurations. They can rapidly incorporate technological advances. The same transport protocol module can be used on a lower capacity machine, where economics may preclude the use of front-end hardware, and also on a larger scale system where a front-end is economically justified.

Figure 1-12 Protocol Migration
Figure 1-13 shows the same canonical module (for example, one that provides delete and kill processing on character strings) reused in two different Streams. This module is typically implemented as a filter, with no downstream service interface. In both cases, a tty interface is presented to the Stream's user process because the module is nearest to the Stream head.

Figure 1-13 Module Reusability
| Home |
|---|
| Contents | Previous Chapter | Next Chapter | Index | Glossary |