STREAMS Programming Guide

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

To a user application, a STREAMS device resembles an ordinary character I/O device, as it has one or more nodes associated with it in the file system, and is opened by calling open(2).

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.

Controlling Data Flow

If the stream exerts flow control, the write(2) call blocks until flow control has been relieved, unless the file has been specifically advised not to. open(2) or fcntl(2) can be used to control this nonblocking behavior.

Simple Stream Example

Example 1–1 shows how an application might use a simple stream. Here, the user program interacts with a communications device that provides point-to-point data transfer between two computers. Data written to the device is transmitted over the communications line, and data arriving on the line is retrieved by reading from the device.


Example 1–1 Simple Stream

#include <sys/fcntl.h>
#include <stdio.h>

main()
{
		char buf[1024];
		int fd, count;
	
		if ((fd = open("/dev/ttya", O_RDWR)) < 0) {
			perror("open failed");
			exit(1);
		}
		while ((count = read(fd, buf, sizeof(buf))) > 0) {
			if (write(fd, buf, count) != count) {
				perror("write failed");
				break;
			}
		}
		exit(0);
}

In this example, /dev/ttya identifies an instance of a serial 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–3 shows the state of the stream following the call to open(2).

Figure 1–3 Stream to Communications Driver

Diagram shows the state of the stream in the Simple Stream
example after a call to open.

This example illustrates a simple loop, with the application reading data from the communications device, then writing the input back to the same device, echoing all input back over the communications line. The program reads up to 1024 bytes at a time, and then writes the number of bytes just read.

read(2) returns the available data, which can contain fewer than 1024 bytes. If no data is currently available at the stream head, read(2) blocks until data arrives.


Note –

The application program must loop on read(2) until the desired number of bytes are read. The responsibility for the application getting all the bytes it needs is that of the application developer, not the STREAMS facilities.


Similarly, the write(2) call attempts to send the specified number of bytes to /dev/ttya. The driver can implement a flow-control mechanism that prevents a user from exhausting system resources by flooding a device driver with data.