STREAMS(流)是系统 V 提供的构造内核设备驱动程序和网络协议包的一种通用方法。流在用户进程和设备驱动程序之间提供了一条全双工通路,下面是流在用户进程和设备驱动程序之间的流图:写到流首的数据将顺流而下传送,由设备驱动程序读到的数据则逆流向上传送;
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)中,只涉及三种消息类型,他们是:
- M_DATA(I/O 的用户数据);
- M_PROTO(协议控制信息);
- M_PCPROTO(高优先级协议控制信息);
流中的消息都有一个排队优先级:
- 高优先级消息(最高优先级);
- 优先级波段消息;
- 普通消息(最低优先级);
普通信消息是优先级波段为 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,第三个参数是一个整型值,则其值成为该流新的写模式,我们可以先获取当前写模式值,然后修改它,则进行设置。目前只定义了两个写模式值。
- SNDZERO:对管道和 FIFO 的0长度 write 会造成顺流传送一个0长度消息。按系统默认,0长度写不发送消息。
- SNDPIPE:在流上已出错后,若调用 write 和 putmsg,则向调用进程发送 SIGPIPE 信息。
读模式
读STREAMS设备有两个潜在的问题:
- 如果读到流中消息的记录边界将会怎样?
- 如果调用 read,而流中下一个消息由控制信息又将如何?
调用 ioctl 时,若将 request 设置成 I_GRDOPT,第三个参数又是指向一个整型单元的指针,则对该流的当前读模式在该整型单元中返回。如果将 request 设置为 I_SRDOPT,第三个参数是整型值,则将该流的读模式设置为该值。读模式可由下列三个常量指定:
- RNORM:普通,字节流模式,如上述这是默认模式。
- RMSGN:消息不丢弃模式,read从流中取数据知道读到所要求的字节数,或者到达消息边界。如果某次read只用了消息的一部分,则其余部分仍留在流中,以供下一次读。
- RMSGD:消息丢弃模式,这与不丢弃模式的区别是,如果某次只用了消息的一部分,则余下部分就被丢弃,不再使用。
在读模式中还可指定另外三个变量,以便设置在读到流中包含协议控制信息的消息时read的处理方法:任一时刻,智能设置一种消息读模式和一种协议读模式,默认读模式是:(RNORM | RPROTNORM)。
- RPROTNORM:协议-普通模式。read 出错返回,errno 设置为 EBADMSG。这是默认模式。
- RPROTDAT:协议-数据模式。read 将控制部分作为数据返回给调用者。
- 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高级环境编程》