![]() |
![]() |
| |
Closing a StreamThe 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 FlowIf 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 ExampleExample 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
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 ![]() 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. How STREAMS Works at the Kernel LevelDevelopers implementing STREAMS device drivers and STREAMS modules use a set of STREAMS-specific functions and data structures. This section describes some basic kernel-level STREAMS concepts. Creating the Stream HeadThe stream head is created when a user process opens a STREAMS device. It translates the interface calls of the user process into STREAMS messages, which it sends to the stream. The stream head also translates messages originating from the stream into a form that the application can process. The stream head contains a pair of queues; one queue passes messages upstream from the driver, and the other passes messages to the driver. The queues are the pipelines of the stream, passing data between the stream head, modules, and driver. Message ProcessingA STREAMS module does processing operations on messages passing from a stream head to a driver or from a driver to a stream head. For example, a TCP module might add header information to the front of data passing downstream through it. Not every stream requires a module. There can be zero or more modules in a stream. Modules are stacked (pushed) onto and unstacked (popped) from a stream. Each module must provide open(), close(), and put() entries and provides a service() entry if the module supports flow control. Like the stream head, each module contains a pair of queue structures, although a module only queues data if it is implementing flow control. Figure 1-4 shows the queue structures Au/Ad associated with Module A ("u" for upstream "d" for downstream) and Bu/Bd associated with Module B. The two queues operate completely independently. Messages and data can be shared between upstream and downstream queues only if the module functions are specifically programed to share data. Within a module, one queue can refer to the messages and data of the opposing queue. A queue can directly refer to the queue of the successor module (adjacent in the direction of message flow). For example, in Figure 1-4, Au (the upstream queue from Module A) can reference Bu (the upstream queue from Module B). Similarly Queue Bd can reference Queue Ad. Figure 1-4 Stream in More Detail ![]() Both queues in a module contain messages, processing procedures, and private data. A module is initialized by either an I_PUSH ioctl(2), or pushed automatically during an open if a stream has been configured by the autopush(1M) mechanism, or if that stream is reopened. A module is disengaged by close or the I_POP ioctl(2). Structure of a STREAMS Device DriverSTREAMS device drivers are structurally similar to STREAMS modules and character device drivers. The STREAMS interfaces to driver routines are identical to the interfaces used for modules. For instance they must both declare open, close, put, and service entry points. There are some significant differences between modules and drivers. A driver:
Both drivers and modules can pass signals, error codes, and return values to processes using message types provided for that purpose. Message ComponentsAll kernel-level input and output under STREAMS is based on messages. STREAMS messages are built in sets of three:
Each data block and data pair can be referenced by one or more message headers. The objects passed between STREAMS modules are pointers to messages. Messages are sent through a stream by successive calls to the put procedure of each module or driver in the stream. Messages can exist as independent units, or on a linked list of messages called a message queue. STREAMS utility routines enable developers to manipulate messages and message queues. All STREAMS messages are assigned message types to indicate how they will be used by modules and drivers and how they will be handled by the stream head. Message types are assigned by the stream head, driver, or module when the message is created. The stream head converts the system calls read, write, putmsg, and putpmsg into specified message types, and sends them downstream. It responds to other calls by copying the contents of certain message types that were sent upstream. | |
| |