STREAMS provides a basic message-passing scheme based on the following concepts:
A STREAMS message consists of one or more linked message blocks. That is, the first message block of a message may be attached to other message blocks that are part of the same message. Multiple blocks in a message can occur, for example, as the result of processing that adds header or trailer data to the data contained in the message, or because of size limitations in the message buffer that cause the data to span multiple blocks. When a message is composed of multiple message blocks, the message type of the first block determines the type of the entire message, regardless of the types of the attached message blocks.
STREAMS allocates a message as a single block containing a buffer of a certain size. If the data for a message exceeds the size of the buffer containing the data, the procedure can allocate a new block containing a larger buffer, copy the current data to it, insert the new data, and deallocate the old block. Alternatively, the procedure can allocate an additional (smaller) block, place the new data in the new message block, and link it after or before the initial message block. Both alternatives yield one new message.
Messages can exist standalone when the message is being processed by a procedure. Alternatively, a message can await processing on a linked list of messages, called a message queue, in a QUEUE. In the Message Queue diagram , Message 1 is linked to Message 2.
Figure 10-12. Message Queue. This diagram shows the queue header on the left which is bordered by message 1. The message block (type) in message 1 has a two-way arrow connected to the queue header and also another two-way arrow to the message block in message 2. Below this message block (type) an arrow points to another message block and that block, in turn, points to another message block within the message 1 area. The lowest message block has an arrow that points downward. To the right of message 1, is message 2. A two-way arrow exits the message block (type) on the right and continues to the next message. Below the message block (type) of message 2, an arrow points to a message block. That message block has an arrow that points downward.
When a message is queued, the first block of the message contains links to preceding and succeeding messages on the same message queue, in addition to containing a 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.
STREAMS maintains its own storage pool for messages. A procedure can request the allocation of a message of a specified size at one of three message pool priorities. The allocb utility returns a message containing a single block with a buffer of at least the size requested, provided there is a buffer available at the priority requested. When requesting priority for messages, developers must weigh the process' need for resources against the needs of other processes on the same machine.
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 will convert certain system calls to specified message types and send them downstream. It will also respond to other calls by copying the contents of certain message types that were sent upstream. Messages exist only in the kernel, so a user process can only send and receive buffers. The process is not explicitly aware of the message type, but it may be aware of message boundaries, depending on the system call used (see the distinction between the getmsg system call and the read subroutine in "Sending and Receiving Messages" ).
Most message types are internal to STREAMS and can only be passed from one STREAMS module to another. A few message types, including M_DATA, M_PROTO, and M_PCPROTO, can also be passed between a stream and user processes. M_DATA messages carry data both within a stream and between a stream and a user process. M_PROTO and M_PCPROTO messages carry both data and control information. However, the distinction between control information and data is generally determined by the developer when implementing a particular stream. Control information includes two types of information: service interface information and condition or status information. Service interface information is carried between two stream entities that present service interfaces. Condition or status information can be sent between any two stream entities regardless of their interface. An M_PCPROTO message has the same general use as an M_PROTO message, but the former moves faster through a stream.
The STREAMS scheduler operates strictly in a first-in-first-out (FIFO) manner so that each QUEUE service procedure receives control in the order it was scheduled. When a service procedure receives control, it may encounter multiple messages on its message queue. This buildup can occur if there is a long interval between the time a message is queued by a put procedure and the time that the STREAMS scheduler calls the associated service procedure. In this interval, there can be multiple calls to the put procedure causing multiple messages. The service procedure processes all messages on its message queue unless prevented by flow control. Each message must pass through all the modules connecting its origin and destination in the stream.
If service procedures were used in all QUEUEs and there was no message priority, the most recently scheduled message would be processed after all the other scheduled messages on all streams had been processed. In certain cases, message types containing urgent information (such as a break or alarm condition) must pass through the stream quickly. To accommodate these cases, STREAMS assigns priorities to the messages. These priorities order the messages on the queue. Each message has a priority band associated with it. Ordinary messages have a priority of 0. Message priorities range from 0 (ordinary) to 255 (highest). This provides up to 256 bands of message flow within a stream. (See the Message Ordering on a Queue diagram.)
Figure 10-13. Message Ordering on a Queue. This diagram shows the head of the continuum of message ordering on the right and the tail on the left. At the head, are high-priority messages, followed by priority band n messages. The next box of dots represent all bands between n and 2. Following (to the left) are priority band 2 messages and priority band 1 messages. On the tail (left) end are normal band 0 messages.
High-priority messages are not affected by flow control. Their priority band is ignored. The putq utility places high priority messages at the head of the message queue, followed by priority band messages and ordinary messages. STREAMS prevents high-priority messages from being blocked by flow control and causes a service procedure to process them ahead of all other messages on the procedure queue. This procedure results in the high-priority message moving through each module with minimal delay.
Message queues are generally not present in a QUEUE unless that QUEUE contains a service procedure. When a message is passed to the putq utility to schedule the message for service procedure processing, the putq utility places the message on the message queue in order of priority. High-priority messages are placed ahead of all ordinary messages, but behind any other high-priority messages on the queue. Other messages are placed after messages of the same priority that are already on the queue. STREAMS utilities deliver the messages to the processing service procedure in a FIFO manner within each priority band. The service procedure is unaware of the message priority and receives the next message.
Message priority is defined by the message type; after a message is created, its priority cannot be changed. Certain message types come in equivalent high/ordinary priority pairs (for example, M_PCPROTO and M_PROTO), so that a module or device driver can choose between the two priorities when sending information.
The putmsg system call is a STREAMS-related system call that sends messages. It is similar to the write subroutine. The putmsg system call provides a data buffer that is converted into an M_DATA message. The system call can also provide a separate control buffer to be placed into an M_PROTO or M_PCPROTO block. The write subroutine provides byte-stream data to be converted into M_DATA messages.
The getmsg system call is a STREAM-related system call that accepts messages. It is similar to the read subroutine. One difference between the two calls is that the read subroutine accepts only data (messages sent upstream to the stream head as message type M_DATA), such as the characters entered from the terminal. The getmsg system call can simultaneously accept both data and control information (that is, a message sent upstream as type M_PROTO or M_PCPROTO). The getmsg system call also differs from the read subroutine in that it preserves message boundaries so that the same boundaries exist above and below the stream head (that is, between a user process and a stream). The read subroutine generally ignores message boundaries, processing data as a byte stream.
Certain streamio operations, such as the I_STR operation, also cause messages to be sent or received on the stream. The I_STR operation provides the general ioctl capability of the character input/output subsystem. A user process above the stream head can issue the putmsg system call, the getmsg system call, the I_STR operation, and certain other STREAMS-related functions. Other streamio operations perform functions that include changing the state of the stream head, pushing and popping modules, or returning special information.
In addition to message types that explicitly transfer data to a process, some messages sent upstream result in information transfer. When these messages reach the stream head, they are transformed into various forms and sent to the user process. The forms include signals, error codes, and call return values.