《unix高级环境编程》高级 I/O——STREAMS(流)

        STREAMS(流)是系统 V 提供的构造内核设备驱动程序和网络协议包的一种通用方法。流在用户进程和设备驱动程序之间提供了一条全双工通路,下面是流在用户进程和设备驱动程序之间的流图:写到流首的数据将顺流而下传送,由设备驱动程序读到的数据则逆流向上传送;

《unix高级环境编程》高级 I/O——STREAMS(流)_第1张图片

STREAMS 消息

        STREAMS 的所有输入和输出都是基于消息,流首和用户进程使用 read、write、ioctl、getmsg、getpmsg、putmsg 和 putpmsg 交换信息。在流首、各处理模块和设备驱动程序之间,消息可顺流而下,也可逆流而上。

       在用户进程和流首之间,消息由以下几部分组成:消息类型、控制信息和数据;其中控制信息和数据由以下结构指定:

struct strbuf
{
       int maxlen;  /* size of buffer */
       int len;         /* number of bytes currently in buffer */
       char *buf;    /* pointer to buffer */
};
 
        当使用putmsg 或 putpmsg 发送消息时,len 指定缓冲区中数据的字节数;当使用 getmsg 或 getpmsg 接收消息时,maxlen 指定缓冲区长度,而 len 则由内核设置为存放缓冲区的数据量;消息长度允许为0,len 为 -1 时说明没有控制信息和数据。

       在我们所使用的函数(read,write,getmsg,getpmsg,putmsg 和 putpmsg)中,只涉及三种消息类型,他们是:

  1. M_DATA(I/O 的用户数据);
  2. M_PROTO(协议控制信息);
  3. M_PCPROTO(高优先级协议控制信息);

       流中的消息都有一个排队优先级:

  1. 高优先级消息(最高优先级);
  2. 优先级波段消息;
  3. 普通消息(最低优先级);

        普通信消息是优先级波段为 0 的消息,优先级波段可在 1~255 之间,波段愈高,优先级也愈高,在任何时刻流首只有一个高优先级消息排队,若在流首读队列已有一个高优先级消息,则另外的高优先级消息会被丢弃。

putmsg 和 putpmsg 函数

         putmsg 和 putpmsg 函数用于 STREAMS 消息写至流中,这两个函数的区别是后者允许对消息指定一个优先级波段。

/* 流 */
/*
 * 函数功能:将STREAMS消息写至流;
 * 返回值:若成功则返回0,若出错则返回-1;
 * 函数原型:
 */
#include <stropts.h>
int putmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int flags);
int putpmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int band, int flags);
/*
 * 说明:
 * 对流使用write函数等价于不带任何控制信息、flags为0的putmsg函数;
 * 这两函数可以产生三种不同优先级的消息:普通、优先级波段和高优先级;
 */

STREAMS 的 ioctl 操作

        ioctl 函数如下:

/* Perform the I/O control operation specified by REQUEST on FD.
   One argument may follow; its presence and type depend on REQUEST.
   Return value depends on REQUEST.  Usually -1 indicates error.  */
 int ioctl (int __fd, unsigned long int __request, ...) ;
        ioctl 的第二个参数request说明执行哪一个操作。所有request都以I_开始。第三个参数的作用与request有关,有时它是一个整型值,有时它是指向一个整型或一个数据结构的指针。

/*
 * 函数功能:判断描述符是否引入一个流;
 * 返回值:若为STREAMS设备则返回1,否则返回0;
 * 函数原型:
 */
#include <stropts.h>
int isastream(int filedes);
/*
 * 说明:
 * 该函数是通过ioctl函数来进行的,可有如下实现:
 */
#include <stropts.h>
#include <unistd.h>
int isastream(int fd)
{
    return(ioctl(fd, I_CANPUT, 0) != -1);
}

写模式

        可以使用两个 ioctl 命令取得和设置一个流的写模式,如果将 request 设置为 I_GWPORT,第三个参数设置为指向一个整型变量的指针,则该流的当前写模式在该整型变量中返回。如果将 request 设置为 I_SWPORT,第三个参数是一个整型值,则其值成为该流新的写模式,我们可以先获取当前写模式值,然后修改它,则进行设置。目前只定义了两个写模式值。

  1. SNDZERO:对管道和 FIFO 的0长度 write 会造成顺流传送一个0长度消息。按系统默认,0长度写不发送消息。
  2. SNDPIPE:在流上已出错后,若调用 write 和 putmsg,则向调用进程发送 SIGPIPE 信息。

读模式

        读STREAMS设备有两个潜在的问题:

  1. 如果读到流中消息的记录边界将会怎样?
  2. 如果调用 read,而流中下一个消息由控制信息又将如何?
        对第一种情况的默认处理模式称为字节流模式。read 从流中取数据直至满足了所要求的字节数,或者已经不再有数据。在这种模式中,忽略流中消息的边界。第二种情况的默认处理是,read 出错返回。可以改变这两种默认处理模式。
        调用 ioctl 时,若将 request 设置成 I_GRDOPT,第三个参数又是指向一个整型单元的指针,则对该流的当前读模式在该整型单元中返回。如果将 request 设置为 I_SRDOPT,第三个参数是整型值,则将该流的读模式设置为该值。读模式可由下列三个常量指定:
  1. RNORM:普通,字节流模式,如上述这是默认模式。
  2. RMSGN:消息不丢弃模式,read从流中取数据知道读到所要求的字节数,或者到达消息边界。如果某次read只用了消息的一部分,则其余部分仍留在流中,以供下一次读。
  3. RMSGD:消息丢弃模式,这与不丢弃模式的区别是,如果某次只用了消息的一部分,则余下部分就被丢弃,不再使用。

         在读模式中还可指定另外三个变量,以便设置在读到流中包含协议控制信息的消息时read的处理方法:任一时刻,智能设置一种消息读模式和一种协议读模式,默认读模式是:(RNORM | RPROTNORM)。

  1. RPROTNORM:协议-普通模式。read 出错返回,errno 设置为 EBADMSG。这是默认模式。
  2. RPROTDAT:协议-数据模式。read 将控制部分作为数据返回给调用者。
  3. RPROTDIS:协议-丢弃模式。read 丢弃消息中的控制信息。但是返回消息中的数据。

getmsg 和 getpmsg 函数


/*
 * 函数功能:将从流读STREAMS消息;
 * 返回值:若成功则返回非负值,若出错则返回-1;
 * 函数原型:
 */
#include <stropts.h>
int getmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int *flagptr);
int getpmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *datptr, int *bandptr, int *flagptr);
/*
 * 说明:
 * 如果flagptr指向的整型单元的值是0,则getmsg返回流首读队列中的下一个消息;
 * 如果下一个消息是最高优先级消息,则在返回时,flagptr所指向的整型单元设置为RS_HIPRI;
 * 如果只希望接收高优先级消息,则在调用getmsg之前必须将flagptr所指向的整型单元设置为RS_HIPRI;
 * getmsg可以设置待接收消息的优先级波段;
 */


参考资料:

《UNIX高级环境编程》

你可能感兴趣的:(IO,高级,流STREAMS)