getmsg is called to retrieve the acknowledgement of the bind request. The acknowledgement message consists of a control part that contains either an OK_ACK or an error_ack structure, and no data part.
The acknowledgement primitives are defined as high-priority messages. Messages are queued in a first-in, first-out (FIFO) manner within their priority at the stream head. The STREAMS mechanism allows only one high-priority message per stream at the stream head at one time. Any additional high-priority messages are discarded on reaching the stream head. (There can be only one high-priority message present on the stream head read queue at any time.) High-priority messages are particularly suitable for acknowledging service requests when the acknowledgement should be placed ahead of any other messages at the stream head.
Before calling getmsg, this routine must initialize the strbuf structure for the control part. buf should point to a buffer large enough to hold the expected control part, and maxlen must be set to indicate the maximum number of bytes this buffer can hold.
Because neither acknowledgement primitive contains a data part, the dataptr argument to getmsg is set to NULL. The flagsp argument points to an integer containing the value RS_HIPRI. This flag indicates that getmsg should wait for a STREAMS high-priority message before returning. This catches the acknowledgement primitives that are priority messages. Otherwise, if the flag is zero, the first message is taken. With RS_HIPRI set, even if a normal message is available, getmsg blocks until a high-priority message arrives.
On return from getmsg, check the len field to ensure that the control part of the retrieved message is an appropriate size. The example then checks the primitive type and takes appropriate actions. An OK_ACK indicates a successful bind operation, and inter_open returns the file descriptor of the open stream. An error_ack indicates a bind failure, and errno is set to identify the problem with the request.
Closing the Service Provider
The next routine in the service interface library example is inter_close, which closes the stream to the service provider.
inter_close(fd) { close(fd); } |
The routine closes the given file descriptor. This causes the protocol driver to free any resources associated with that stream. For example, the driver can unbind the protocol address that had previously been bound to that stream, thereby freeing that address for use by another service user.
Sending Data to the Service Provider
The third routine, inter_snd, passes data to the service provider for transmission to the user at the address specified in addr. The data to be transmitted is contained in the buffer pointed to by buf and contains len bytes. On successful completion, this routine returns the number of bytes of data passed to the service provider; on failure, it returns -1.
In this example, the data request primitive is packaged with both a control part and a data part. The control part contains a unitdata_req structure that identifies the primitive type and the destination address of the data. The data to be transmitted is placed in the data part of the request message.
Unlike the bind request, the data request primitive requires no acknowledgement from the service provider. In the example, this choice was made to minimize the overhead during data transfer. If the putmsg call succeeds, this routine returns the number of bytes passed to the service provider.
Receiving Data
The final routine in Example 3-4, inter_rcv, retrieves the next available data. buf points to a buffer where the data should be stored, len indicates the size of that buffer, and addr points to a long integer where the source address of the data is placed. On successful completion, inter_rcv returns the number of bytes of retrieved data; on failure, it returns -1 and an appropriate UNIX system error value.
getmsg is called to retrieve the data indication primitive, where that primitive contains both a control and data part. The control part consists of a unitdata_ind structure that identifies the primitive type and the source address of the data sender. The data part contains the data itself. In ctlbuf, buf points to a buffer containing the control information, and maxlen indicates the maximum size of the buffer. Similar initialization is done for databuf.
The integer pointed to by flagsp in the getmsg call is set to zero, indicating that the next message should be retrieved from the stream head regardless of its priority. Data arrives in normal priority messages. If there is no message at the stream head, getmsg blocks until a message arrives.
The user's control and data buffers should be large enough to hold any incoming data. If both buffers are large enough, getmsg processes the data indication and returns 0, indicating that a full message was retrieved successfully. However, if neither buffer is large enough, getmsg only returns the part of the message that fits into each user buffer. The remainder of the message is saved for subsequent retrieval (in message non-discard mode), and a positive, nonzero value is returned to the user. A return value of MORECTL indicates that more control information is waiting for retrieval. A return value of MOREDATA indicates that more data is waiting for retrieval. A return value of (MORECTL | MOREDATA) indicates that data from both parts of the message remain. In the example, if the user buffers are not large enough (that is, getmsg returns a positive, nonzero value), the function sets errno to EIO and fails.
The type of the primitive returned by getmsg is checked to make sure it is a data indication (UNITDATA_IND in the example). The source address is then set and the number of bytes of data is returned.
The example presented is a simplified service interface. It shows typical uses of putmsg(2) and getmsg(2). The state transition rules for the interface are not presented and this example does not handle expedited data.
Input and Output Polling
This section describes the synchronous polling mechanism and asynchronous event notification in STREAMS.
User processes can efficiently monitor and control multiple streams with two system calls: poll(2) and the I_SETSIG ioctl(2) command. These calls enable a user process to detect events that occur at the stream head on one or more streams, including receipt of data or messages on the read queue and cessation of flow control on the write queue. Note that poll(2) is usable on any character device file descriptor, not just STREAMS.
To monitor streams with poll(2), a user process issues that system call and specifies the streams and other files to be monitored, the events to check, and the amount of time to wait for an event. poll(2) blocks the process until the time expires or until an event occurs. If an event occurs, it returns the type of event and the descriptor on which the event occurred.
Instead of waiting for an event to occur, a user process can monitor one or more streams while processing other data. To do so, issue the I_SETSIG ioctl(2), specifying a stream and events (as with poll(2)). This ioctl(2) does not block the process and force the user process to wait for the event, but returns immediately and issues a signal when an event occurs. The process calls one of sigaction(2), signal(3C), or sigset(3C) to catch the resulting SIGPOLL signal.
If any selected event occurs on any of the selected streams, STREAMS sends SIGPOLL to all associated requesting processes. The processes have no information on what event occurred on what stream. A signaled process can get more information by calling poll(2).
Synchronous Input and Output
poll(2) provides a mechanism to identify the streams over which a user can send or receive data. For each stream of interest, users can specify one or more events about which they should be notified. The types of events that can be polled are POLLIN, POLLRDNORM, POLLRDBAND, POLLPRI, POLLOUT, POLLWRNORM, POLLWRBAND, which are detailed in Table 3-3.
Table 3-3 Events That Can Be Polled
Event | Description |
---|---|
POLLIN | A message other than high-priority data can be read without blocking. This event is maintained for compatibility with the previous releases of the Solaris operating environment. |
POLLRDNORM | A normal (nonpriority) message is at the front of the stream head read queue. |
POLLRDBAND | A priority message (band > 0) is at the front of the stream head queue. |
POLLPRI | A high-priority message is at the front of the stream head read queue. |
POLLOUT | The normal priority band of the queue is writable (not flow controlled). |
POLLWRNORM | The same as POLLOUT. |
POLLWRBAND | A priority band greater than 0 of a queue downstream. |
Some of the events may not be applicable to all file types. For example, the POLLPRI event usually is not generated when polling a non-STREAMS character device. POLLIN, POLLRDNORM, POLLRDBAND, and POLLPRI are set even if the message is of zero length.
poll(2) checks each file descriptor for the requested events and, on return, indicates which events have occurred for each file descriptor. If no event has occurred on any polled file descriptor, poll(2) blocks until a requested event or timeout occurs. poll(2) takes the following arguments:
An array of file descriptors and events to be polled.
The number of file descriptors to be polled.
The number of milliseconds poll should wait for an event if no events are pending (-1 specifies wait forever).