Overview of STREAMS
This chapter provides a foundation for later chapters. Background and simple definitions are followed by an overview of the STREAMS mechanisms. Because the application developer is concerned with a different subset of STREAMS interfaces than the kernel-level developer, application and kernel levels are described separately.
What Is STREAMS?
STREAMS is a general, flexible programming model for UNIX system communication services. STREAMS defines standard interfaces for character input/output (I/O) within the kernel, and between the kernel and the rest of the UNIX system. The mechanism consists of a set of system calls, kernel resources, and kernel routines.
STREAMS enables you to create modules to provide standard data communications services and then manipulate the modules on a stream. From the application level, modules can be dynamically selected and interconnected. No kernel programming, compiling, and link editing are required to create the interconnection.
STREAMS provides an effective environment for kernel services and drivers requiring modularity. STREAMS parallels the layering model found in networking protocols. For example, STREAMS is suitable for:
Implementing network protocols
Developing character device drivers
Developing network controllers (for example, for an Ethernet card)
I/O terminal services
The fundamental STREAMS unit is the stream. A stream is a full-duplex bidirectional data-transfer path between a process in user space and a STREAMS driver in kernel space. A stream has three parts: a stream head, zero or more modules, and a driver.
Figure 1-1 Simple Stream
The capitalized word "STREAMS" refers to the STREAMS programming model and facilities. The word "stream" refers to an instance of a full-duplex path using the model and facilities between a user application and a driver.
Stream as a Data Path
A stream is a data path that passes data in both directions between a STREAMS driver in kernel space, and a process in user space. An application creates a stream by opening a STREAMS device (see Figure 1-1).
A stream head is the end of the stream nearest the user process. It is the interface between the stream and the user process. When a STREAMS device is first opened, the stream consists of only a stream head and a STREAMS driver.
A STREAMS module is a defined set of kernel-level routines and data structures. A module does "black-box" processing on data that passes through it. For example, a module converts lowercase characters to uppercase, or adds network routing information. A STREAMS module is dynamically pushed on the stream from the user level by an application. Full details on modules and their operation are covered in Chapter 10, STREAMS Modules.
STREAMS Device Driver
A STREAMS device driver is a character device driver that implements the STREAMS interface. A STREAMS device driver exists below the stream head and any modules. It can act on an external I/O device, or it can be an internal software driver, called a pseudo-device driver. The driver transfers data between the kernel and the device. The interfaces between the driver and kernel are known collectively as the Solaris operating environment Device Driver Interface/Driver Kernel Interface (Solaris operating environment DDI/DKI). The relationship between the driver and the rest of the UNIX kernel is explained in Writing Device Drivers. Details of device drivers are explained in Chapter 9, STREAMS Drivers.
Data on a stream is passed in the form of messages. Messages are the means by which all I/O is done under STREAMS. Each stream head, STREAMS module, and driver has a read side and a write side. When messages go from one module's read side to the next module's read side, they are said to be traveling upstream. Messages passing from one module's write side to the next module's write side are said to be traveling downstream. Kernel-level operation of messages is discussed in "Message Components".
Each stream head, driver, and module has its own pair of queues, one queue for the read side and one queue for the write side. Messages are ordered into queues, generally on a first-in, first-out basis (FIFO), according to priorities associated with them. Kernel-level details of queues are covered in "Structure of a Message Queue".
Figure 1-2 Messages Passing Using Queues
Communicating With a STREAMS Device
To communicate with a STREAMS device, an application's process uses read(2), write(2), getmsg(2), getpmsg(2), putmsg(2), putpmsg(2), and ioctl(2) to transmit or receive data on a stream.
From the command line, configure a stream with autopush(1M). From within an application, configure a stream with ioctl(2) as described in streamio(7I).
The ioctl(2) interface performs control operations on and through device drivers that cannot be done through the read(2) and write(2) interfaces. ioctl(2) operations include pushing and popping modules on and off the stream, flushing the stream, and manipulating signals and options. Certain ioctl(2) commands for STREAMS operate on the whole stream, not just the module or driver. The streamio(7I) manual page describes STREAMS ioctl(2) commands. Chapter 4, Application Access to the STREAMS Driver and Module Interfaces details interstream communications.
The modularity of STREAMS allows one or more upper streams to route data into one or more lower streams. This process is defined as multiplexing (mux). Example configurations of multiplexers are described in "Configuring Multiplexed Streams".
Polling within STREAMS enables a user process to detect events occurring at the stream head, specifying the event to look for and the amount of time to wait for it to happen. An application might need to interact with multiple streams. The poll(2) system call enables applications to detect events that occur at the head of one or more streams. Chapter 3, STREAMS Application-Level Mechanisms describes polling.
Message Transfer Flow Control
Flow control regulates the rate of message transfer between the user process, stream head, modules, and driver. With flow control, a module that cannot process data at the rate being sent can queue the data to avoid flooding modules upstream. Flow control is local to each module or driver, and is voluntary. Chapter 8, STREAMS Kernel-Level Mechanisms describes flow control.
When to Use STREAMS
The STREAMS framework is most useful when modularity and configurability are issues. For instance, network drivers, terminal drivers, and graphics I/O device drivers benefit from using STREAMS. Modules can be pushed (added) and popped (removed) to create desired program behavior.
STREAMS is general enough to provide modularity between a range of protocols. It is a major component in networking support utilities for UNIX System V because it facilitates communication between network protocols.
How STREAMS Works--Application Interface
An application opens a STREAMS device, which creates the stream head to access the device driver. The stream head packages the data from the user process into STREAMS messages, and passes it downstream into kernel space. One or more cooperating modules can be pushed on a stream between the stream head and driver to customize the stream and perform any of a range of tasks on the data before passing it on. On the other hand, a stream might consist solely of the stream head and driver, with no module at all.
Opening a Stream
The file system represents each device as a special file. There is an entry in the file for the major device number, identifying the actual device driver that will activate the device. There are corresponding separate minor device numbers for each instance of a particular device, for example, for a particular port on a serial card, or a specific pseudo-terminal such as those used by a windowing application.
Different minor devices of a driver cause a separate stream to be connected between a user process and the driver. The first open call creates the stream; subsequent open calls respond with a file descriptor referencing that stream. If the same minor device is opened more than once, only one stream is created.
However, drivers can support a user process getting a dedicated stream without the application distinguishing which minor device is used. In this case, the driver selects any unused minor device to be used by the application. This special use of a minor device is called cloning. Chapter 9, STREAMS Drivers describes properties and behavior of clone devices.
Once a device is opened, a user process can send data to the device by calling write(2), and receive data from the device by calling read(2). Access to STREAMS drivers using read and write is compatible with the traditional character I/O mechanism. STREAMS-specific applications also can call getmsg(2), getpmsg(2), putmsg(2), and putpmsg(2) to pass data to and from the stream.
Closing a Stream
The close(2) interface closes a device and dismantles the associated stream when the last open reference to the stream is closed. The exit(2) interface terminates the user process and closes all open files.