Sun Microsystems, Inc.
spacerspacer
spacer www.sun.com docs.sun.com |
spacer
black dot
 
 
8.  STREAMS Kernel-Level Mechanisms M_FLUSH Message Handling Flushing According to Priority Bands Flushing Priority Band  Previous   Contents   Next 
   
 

Note that modules and drivers are not required to treat messages as flowing in separate bands. Modules and drivers can view the queue as having only two bands of flow, normal and high priority. However, the latter alternative flushes the entire queue whenever an M_FLUSH message is received.

The field b_flag of the msgb structure provides a way for the stream head to stop M_FLUSH messages from being reflected forever when the stream is used as a pipe. When the stream head receives an M_FLUSH message, it sets the MSGNOLOOP flag in the b_flag field before reflecting the message down the write side of the stream. If the stream head receives an M_FLUSH message with this flag set, the message is freed rather than reflected.

Figure 8-3 Interfaces Affecting Drivers

The set of STREAMS utilities available to drivers are listed in Appendix B, Kernel Utility Interface Summary. No system-defined macros that manipulate global kernel data or introduce structure-size dependencies are permitted in these utilities. So, some utilities that have been implemented as macros in the prior Solaris operating environment releases are implemented as functions in the SunOS 5 system. This does not preclude the existence of both macro and function versions of these utilities. Driver source code should include a header file that picks up function declarations while the core operating system source should include a header file that defines the macros. With the DKI interface, the following STREAMS utilities are implemented as C programming language functions: datamsg(9F), OTHERQ(9F), putnext(9F), RD(9F), and WR(9F).

Replacing macros such as RD with function equivalents in the driver source code allows driver objects to be insulated from changes in the data structures and their size, increasing the useful lifetime of driver source code and objects. Multithreaded drivers are also protected against changes in implementation-specific STREAMS synchronization.

The DKI defines an interface suitable for drivers and there is no need for drivers to access global kernel data structures directly. The kernel function drv_getparm(9F) fetches information from these structures. This restriction has an important consequence. Because drivers are not permitted to access global kernel data structures directly, changes in the contents/offsets of information within these structures will not break objects.

Driver and Module Service Interfaces

STREAMS provides the means to implement a service interface between any two components in a stream, and between a user process and the topmost module in the stream. A service interface is a set of primitives defined at the boundary between a service user and a service provider (see Figure 8-5). Rules define a service and the allowable state transitions that result as these primitives are passed between the user and the provider. These rules are typically represented by a state machine. In STREAMS, the service user and provider are implemented in a module, driver, or user process. The primitives are carried bidirectionally between a service user and provider in M_PROTO and M_PCPROTO messages.

PROTO messages (M_PROTO and M_PCPROTO) can be multiblock. The second through last blocks are of type M_DATA. The first block in a PROTO message contains the control part of the primitive in a form agreed upon by the user and provider. The block is not intended to carry protocol headers. (Upstream PROTO messages can have multiple PROTO blocks at the start of the message, although its use is not recommended. getmsg(2) compacts the blocks into a single control part when sending to a user process.) The M_DATA block contains any data part associated with the primitive. The data part can be processed in a module that receives it, or it can be sent to the next stream component, along with any data generated by the module. The contents of PROTO messages and their allowable sequences are determined by the service interface specification.

PROTO messages can be sent bidirectionally (upstream and downstream) on a stream and between a stream and a user process. putmsg(2) and getmsg(2) system calls are analogous to write(2) and read(2) except that the former allow both data and control parts to be (separately) passed, and they retain the message boundaries across the user-stream interface. putmsg(2) and getmsg(2) separately copy the control part (M_PROTO or M_PCPROTO block) and data part (M_DATA blocks) between the stream and user process.

An M_PCPROTO message normally is used to acknowledge primitives composed of other messages. M_PCPROTO ensures that the acknowledgement reaches the service user before any other message. If the service user is a user process, the stream head will only store a single M_PCPROTO message, and discard subsequent M_PCPROTO messages until the first one is read with getmsg(2).

Figure 8-4 Protocol Substitution

By defining a service interface through which applications interact with a transport protocol, you can substitute a different protocol below the service interface that is completely transparent to the application. In Figure 8-4, the same application can run over the Transmission Control Protocol (TCP) and the ISO transport protocol. Of course, the service interface must define a set of services common to both protocols.

The three components of any service interface are the service user, the service provider, and the service interface itself, as seen in Figure 8-5.

Figure 8-5 Service Interface

Typically, an application makes requests of a service provider using some well-defined service primitive. Responses and event indications are also passed from the provider to the user using service primitives.

Each service interface primitive is a distinct STREAMS message that has two parts, a control part and a data part. The control part contains information that identifies the primitive and includes all necessary parameters. The data part contains user data associated with that primitive.

An example of a service interface primitive is a transport protocol connect request. This primitive requests the transport protocol service provider to establish a connection with another transport user. The parameters associated with this primitive can include a destination protocol address and specific protocol options to be associated with that connection. Some transport protocols also allow a user to send data with the connect request. A STREAMS message is used to define this primitive. The control part identifies the primitive as a connect request and includes the protocol address and options. The data part contains the associated user data.

Service Interface Library Example

The service interface library example presented here includes four functions that enable a user do the following:

  • Establish a stream to the service provider and bind a protocol address to the stream

  • Send data to a remote user

  • Receive data from a remote user

  • Close the stream connected to the provider

First, the structure and constant definitions required by the library are shown in the following code. These typically reside in a header file associated with the service interface.

The defined structures describe the contents of the control part of each service interface message passed between the service user and service provider. The first field of each control part defines the type of primitive being passed.


Example 8-16 Service Interface Library Header File

/*
 * Primitives initiated by the service user.
 */
 #define BIND_REQ                   1   /* bind request */
 #define UNITDATA_REQ               2   /* unitdata request */

/*
 * Primitives initiated by the service provider.
 */
 #define OK_ACK                     3   /* bind acknowledgement */
 #define ERROR_ACK                  4   /* error acknowledgement */
 #define UNITDATA_IND               5   /* unitdata indication */

/*
 * The following structure definitions define the format
 * of the control part of the service interface message
 * of the above primitives.
 */
struct bind_req {                      /* bind request */
 	t_scalar_t    PRIM_type;            /* always BIND_REQ */
 	t_uscalar_t   BIND_addr;            /* addr to bind */
};
 struct unitdata_req {                 /* unitdata request */
 	t_scalar_t    PRIM_type;            /* always UNITDATA_REQ */
 	t_scalar_t    DEST_addr;            /* destination addr */
};

struct ok_ack {                        /* positiv acknowledgement*/
 	t_scalar_t    PRIM_type;            /* always OK_ACK */
};

struct error_ack {                     /* error acknowledgement */
 	t_scalar_t    PRIM_type;            /* always ERROR_ACK */
 	t_scalar_t    UNIX_error;           /* UNIX systemerror code */
};

struct unitdata_ind {                  /* unitdata indication */
 	t_scalar_t    PRIM_type;            /* always UNITDATA_IND */
 	t_scalar_t    SRC_addr;             /* source addr */
};

/* union of all primitives */
union primitives {
 	t_scalar_t             type;
 	struct bind_req        bind_req;
 	struct unitdata_req    unitdata_req;
 	struct ok_ack          ok_ack;
 	struct error_ack       error_ack;
 	struct unitdata_ind    unitdata_ind;
};

/* header files needed by library */
#include <stropts.h>
#include <stdio.h>
#include <errno.h>

Five primitives are defined. The first two represent requests from the service user to the service provider.

 
 
 
  Previous   Contents   Next