《UNIX环境高级编程》笔记--STREAMS

STREAMS(流)是system V提供的构造内核设备驱动程序和网络协议包的一种通用方法。

流在用户进程和设备驱动程序之前提供了一条全双工通路。流无需和实际硬件设备直接会话,流也可以用来构造伪设备驱动

程序,下图是一个简单流的基本构造。

《UNIX环境高级编程》笔记--STREAMS_第1张图片

在流首之下可以压入处理模块,这个可以用ioctl命令实现,下图表示包含一个处理模块的流。各方框之间用两根带箭头的线

连接,以突出流的全双工特性,并强调两个方向的处理是相互独立进行的。

《UNIX环境高级编程》笔记--STREAMS_第2张图片

任意数量的处理模块可以压入流,STREAMS模块是作为内核的一部分执行的,这类似于设备驱动程序。当构造内核时,STREAMS

模块联编进入内核。

可以使用之前说明的函数访问流:open,close,read,write和ioctl。另外,在SVR3内核中增加了3个支持流的新函数(getmsg, 

putmsg和poll),在SVR4中又加了两个处理流内不同优先级波段消息的函数(getpmsg和putpmsg)。

打开(open)流使用的路径名参数通常在/dev目录之下,所有STREAMS设备都是字符特殊文件。

我们可以自己编写STREAMS处理模块并将它们压入流中。

1.STREAMS消息

STREAMS的所有输入输出都是基于消息的,流首和用户进程使用read,write,ioctl,getmsg,getpmsg,putmsg和putpmsg交换

消息。

在用户进程和流首之间,消息由下列几部分组成:消息类型,可选择的控制信息以及可选择的数据。

下表列出了对应于write,putmsg和putpmsg的不同参数锁产生的不同消息类型。

《UNIX环境高级编程》笔记--STREAMS_第3张图片

控制信息和数据由strbuf结构指定:

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(IO的用户数据)
  • M_PROTO(协议控制信息)
  • M_PCPROTO(高优先级协议控制信息)
流中的消息都有一个排队优先级:
  • 高优先级消息(最高优先级)
  • 优先级波段消息
  • 普通消息(最低优先级)
普通消息是优先级波段为0的消息,优先级波段消息的波段可在1~255之间。波段越高,优先级也越高,高优先级消息的特殊性
在于,在任何时刻流首只有一个高优先级消息排队,在流首读队列已有一个高优先级消息时,另外的高优先级消息会被丢弃。

2.putmsg和putpmsg函数

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

#include<stropts.h>
int putmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *dataptr, int flag);
int putpmsg(int filedes, const struct strbuf *ctlptr, const struct strbuf *dataptr, int band, int flag);
//成功返回0,出错返回-1.
对流可以使用write函数,它等效于不带任何控制信息,flag为0的putmsg。

3.STREAMS ioctl操作

之前提到过ioctl函数,它能做其他IO函数不能处理的事情,STREAMS系统继承了这种传统。在linux和solars中,使用ioctl

可对流执行将近40中不同操作。头文件<stropts.h>包含在使用这些操作的C代码中,ioctl的第二个参数request说明执行哪一

个操作。所有request都以I_开始。第三个参数的作用与request有关,有时它是一个整型值,有时它是指向一个整型或一个

数据结构的指针。

比如可以使用ioctl函数检查描述符是否引用STREAMS设备:

return(ioctl(fd,I_CANPUT,0) != -1); //若为STREAMS设备则返回1,否则返回0

还可以使用ioctl函数获取已压入该流所有模块的名字,包含最顶端的驱动程序,此时参数request是I_LIST,第三个参数应当

是指向str_list结构的指针。

struct str_list{

int sl_nmods; //number of entries int array

struct str_mlist *sl_modlist; //ptr to first element of array

}

其中str_mlist定义如下:

struct str_mlist{

char l_name[FIMNAMESZ+1]; //null terminated module name

}

如果ioctl的第三个参数是0,则该函数返回值是模块数,而不是模块名,我们先用这种ioctl调用确定模块数,然后再分配所

要求的str_mlist结构数。

4.写模式

可以用两个ioctl命令取得和设置一个流的写模式,如果将request设置为I_GWPORT,第三个参数设置为指向一个整型变量

的指针,则该流的当前写模式在该整型变量中返回。如果将request设置为I_SWPORT,第三个参数是一个整型值,则其值

成为该流新的写模式,我们可以先获取当前写模式值,然后修改它,则进行设置。

目前只定义了两个写模式值。

SNDZERO:对管道和FIFO的0长度write会造成顺流传送一个0长度消息。按系统默认,0长度写不发送消息。

SNDPIPE:在流上已出错后,若调用write和putmsg,则向调用进程发送SIGPIPE信息。

5.getmsg和getpmsg函数

使用read,getmsg或getpmsg函数从流首读STREAMS消息。

#include<stropts.h>
int getmsg(int filedes, struct strbuf *restrict ctlptr, struct strbuf *restrict dataptr, int *restrict flagptr);
int getpmsg(int filedes, struct strbuf *restrict ctlptr, struct strbuf *restrict dataptr, int *restrict bandptr, int *restrict flagptr);
//若成功返回非负值,出错返回-1.
flagptr和bandptr是指向整型的指针,在调用之前,这两个指针所指向的整型单元中应设置成希望的消息类型,在返回时,此

整型变量设置为所读到的消息的类型。

如果flagptr指向的整型单元的值是0,则getmsg返回流首读队列中的下一个消息。如果下一个消息是高优先级消息,则在返回

时,flagptr所指向的整型单元设置为RS_HIPRI。如果希望只接收高优先级消息,则在调用getmsg之前必须将flagptr所指向的

整型单元设置为RS_HIPRI.

getpmsg使用一个不同的常量集。为了只接收高优先级消息,我们可以将flagptr指向的整型单元设置为MSG_HIPRI。为了只接收

某个优先级波段或以上波段的消息,我们可将该整型单元设置为MSG_BAND,然后将bandptr指向的整型单元设置为该波段的非

0优先级值。如果只希望接收第1个可用消息,则可将flagptr指向的整型单元设置为MSG_ANY;在返回时,该整型值将改写为

MSG_HIPRI或MSG_BAND,这取决于接收到的消息的类型。如果取到的消息并非高优先级的消息,那么bandptr指向的整型将包括

消息的优先级波段值。

如果ctlptr是null,或ctlptr->maxlen是-1,那么消息的控制部分仍保留在流首读队列中,我们将不处理它。类似地,如果dataptr是

null,或者dataptr->maxlen是-1,那么消息的数据部分仍保留在流首读队列中,我们也不处理它。否则,将按照缓冲区的容量取到

消息中尽可能多的控制和数据部分,余下部分仍留在队首,等待下次取用。

如果getmsg和getpmsg调用取到一消息,那么返回值是0,如果消息控制部分中有一些余留在流首读队列中,那么返回常量MORECTL。

类似地,如果消息数据中有一些余留在流首读队列中,那么返回常量MOREDATA。如果控制和数据都有一些余留,则返回的常量值

是 MOREDATA | MORECTL。

6.读模式

如果读STREAMS设备会发生些什么呢?有两个潜在的问题:

1.如果读到流中消息的记录边界将会怎样?

2.如果调用read,而流中下一个消息由控制信息又将如何?

对第一种情况的默认处理模式称为字节流模式。read从流中取数据直至满足了所要求的字节数,或者已经不再有数据。在这种

模式中,忽略流中消息的边界。

第二种情况的默认处理是,read出错返回。

可以改变这两种默认处理模式。

调用ioctl时,若将request设置成I_GRDOPT,第三个参数又是指向一个整型单元的指针,则对该流的当前读模式在该整型单元中

返回。如果将request设置为I_SRDOPT,第三个参数是整型值,则将该流的读模式设置为该值。读模式可由下列三个常量指定:

RNORM:普通,字节流模式,如上述这是默认模式。

RMSGN:消息不丢弃模式,read从流中取数据知道读到所要求的字节数,或者到达消息边界。如果某次read只用了消息的一部分,

则其余部分仍留在流中,以供下一次读。

RMSGD:消息丢弃模式,这与不丢弃模式的区别是,如果某次只用了消息的一部分,则余下部分就被丢弃,不再使用。

在读模式中还可指定另外三个变量,以便设置在读到流中包含协议控制信息的消息时read的处理方法。

RPROTNORM:协议-普通模式。read出错返回,errno设置为EBADMSG。这是默认模式。

RPROTDAT:协议-数据模式。read将控制部分作为数据返回给调用者。

RPROTDIS:协议-丢弃模式。read丢弃消息中的控制信息。但是返回消息中的数据。

任一时刻,智能设置一种消息读模式和一种协议读模式,默认读模式是:(RNORM |RPROTNORM)。

你可能感兴趣的:(《UNIX环境高级编程》笔记--STREAMS)