STREAMS Application-Level Mechanisms
The previous chapters described the components of a stream from an application level. This chapter explains how those components work together. It shows how the kernel interprets system calls being passed from an application, so that driver and module developers can know what structures are being passed.
Messages are the communication medium between the user application process and the various components of the stream. This chapter describes the path they travel and the changes that occur to them. Chapter 8, STREAMS Kernel-Level Mechanisms covers the underlying mechanics of the kernel.
The put(9E) and srv(9E) interfaces process messages as they pass through the queue. Messages are generally processed by type, resulting in a modified message, one or more new messages, or no message at all. The message usually continues in the same direction it was passing through the queue, but can be sent in either direction. A put(9E) procedure can place messages on its queue as they arrive, for later processing by the srv(9E) procedure. For a more detailed explanation of put(9E) and srv(9E), see Chapter 8, STREAMS Kernel-Level Mechanisms.
Some kernel operations are explained here to show you how to manipulate the driver or module appropriately.
STREAMS messages differ according to their intended purpose and their queueing priority. The contents of certain message types can be transferred between a process and a stream using system calls. Appendix A, Message Types describes message types in detail.
Control of Stream Head Processing
The stream head responds to a message by altering the processing associated with certain system calls. Six stream head characteristics can be modified. Four characteristics correspond to fields contained in queue (packet sizes -- minimum and maximum, and watermarks -- high and low). Packet sizes are discussed in this chapter. Watermarks are discussed in "Flush Handling" in Chapter 4, Application Access to the STREAMS Driver and Module Interfaces.
The read options (so_readopt) specify two sets of three modes that can be set by the I_SRDOPT ioctl(2) (see streamio(7I)). Byte-stream mode approximately models pipe data transfer. Message nondiscard mode is similar to a TTY in canonical mode.
The first set of bits, RMODEMASK, deals with data and message boundaries, as shown in Table 3-1.
Table 3-1 Data and Message Boundaries
The second set of bits, RPROTMASK, specifies the treatment of protocol messages by the read(2) system call as shown in Table 3-2.
Table 3-2 How read(2) Treats Protocol Messages
|Normal protocol (RPROTNORM)|
|Protocol discard (RPROTDIS)|
|Protocol data (RPROTDAT)|
Message Queueing and Priorities
Any delay in processing messages causes message queues to grow. Normally, queued messages are handled in a first-in, first-out (FIFO) manner. However, certain conditions can require that associated messages (for instance, an error message) reach their stream destination as rapidly as possible. For this reason messages are assigned priorities using a priority band associated with each message. Ordinary messages have a priority of zero. High-priority messages are high priority by nature of their message type. Their priority band is ignored. By convention, they are not affected by flow control. Figure 3-1 shows how messages are ordered in a queue according to priority.
Figure 3-1 Message Ordering in a Queue
When a message is queued, it is placed after the messages of the same priority already in the queue (in other words, FIFO within their order of queueing). This affects the flow-control parameters associated with the band of the same priority. Message priorities range from 0 (normal) to 255 (highest). This provides up to 256 bands of message flow within a stream. Expedited data can be implemented with one extra band of flow (priority band 1) of data. This is shown in Figure 3-2.
Figure 3-2 Message Ordering With One Priority Band
Controlling Data Flow and Priorities
The I_FLUSHBAND, I_CKBAND, I_GETBAND, I_CANPUT, and I_ATMARK ioctl(2)s support multiple bands of data flow. The I_FLUSHBAND ioctl(2) allows a user to flush a particular band of messages. "Flush Handling" discusses it in more detail.
ioctl (fd, I_CKBAND, pri);
The call returns 1 if a message of priority pri exists on the stream head read queue and 0 if no message of priority pri exists. If an error occurs, -1 is returned. Note that pri should be of type int.
ioctl (fd, I_GETBAND, prip);
The call results in the integer referenced by prip being set to the priority band of the message on the front of the stream head read queue.
ioctl (fd, I_CANPUT, pri);
The return value is 0 if the priority band pri is flow controlled, 1 if the band is writable, and -1 on error.
A module or driver can mark a message. This supports the ability of the Transmission Control Protocol (TCP) to indicate to the user the last byte of out-of-band data. Once marked, a message sent to the stream head causes the stream head to remember the message. A user can check whether the message on the front of its stream head read queue is marked with the I_ATMARK ioctl(2). If a user is reading data from the stream head, there are multiple messages on the read queue, and one of those messages is marked, the read(2) terminates when it reaches the marked message and returns the data only up to the marked message. Successive reads can return the rest of the data. Chapter 4, Application Access to the STREAMS Driver and Module Interfaces discusses this in more detail.
ioctl (fd, I_ATMARK, flag);
where flag can be either ANYMARK or LASTMARK. ANYMARK indicates that the user wants to check whether any message is marked. LASTMARK indicates that the user wants to see whether the message is the one and only one marked in the queue. If the test succeeds, 1 is returned. On failure, 0 is returned. If an error occurs, -1 is returned.
Accessing the Service Provider
The first routine presented, inter_open, opens the protocol driver device file specified by path and binds the protocol address contained in addr so that it can receive data. On success, the routine returns the file descriptor associated with the open stream; on failure, it returns -1 and sets errno to indicate the appropriate UNIX system error value. Example 3-1 shows the inter_open routine.
After opening the protocol driver, inter_open packages a bind request message to send downstream. putmsg is called to send the request to the service provider. The bind request message contains a control part that holds a bind_req structure, but it has no data part. ctlbuf is a structure of type strbuf, and it is initialized with the primitive type and address. Notice that the maxlen field of ctlbuf is not set before calling putmsg. That is because putmsg ignores this field. The dataptr argument to putmsg is set to NULL to indicate that the message contains no data part. The flags argument is 0, which specifies that the message is not a high-priority message.
After inter_open sends the bind request, it must wait for an acknowledgement from the service provider, as Example 3-2 shows.