STREAMS

 

 STREAMS()是系统V提供的构造内核设备驱动程序和网络协议包的一种通用方法,对STREAMS进行讨论的目的是为了理解系统V的终端接口,I/O多路转接中poll(轮询)函数的使用,以及基于STREAMS的管道和命名管道的实现。

 

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

    

 STREAMS模块是作为内核的一部分执行的,这类似于设备驱动程序。它在内核中也支持静态和动态挂入内核,或则从内核中卸载。

 STREAMS设备都是字符特殊文件。

 

一、 STREAMS消息

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

 

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

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

为什么需要传送控制信息和数据?

提供这两者使我们可以实现用户进程和服务之间的接口。

 

有约25中不同类型的消息,但是只有少数几种用于用户进程和流首之间,其余的只在内核中顺流、逆流传达。在我们所使用的函数read,write, getmsg,getpmsg,putmsgputpmsg中,只涉及三中类型消息:

l         M_DATAI/O的用户数据)

l         M_PROTO(协议控制信息)

l         M_PCPROTO(高优先级协议控制信息)

流中的消息都有一个排队优先级:高优先级消息(最高优先级)、优先级波段消息、普通优先级消息(最低优先级)。普通消息是优先级波段为0的消息。优先级波段消息的波段可在1-255之间,波段愈高,优先级也愈高。高优先级消息的特殊性在于,在任何时刻,流首只有一个高优先级消息排队。在流首读队列已有一个高优先级消息时,另外的高优先级消息会被丢弃。

 

每个STREAMS模块都有两个输入队列:一个接收来自它上面模块的信息,这种消息从流首向驱动程序顺流传送;另一个接收来自它下面模块的消息,这种消息从驱动程序向流首逆流传送。在输入队列中的消息,按优先级从高到低排列。

 

二、putmsgputpmsg函数

功能:用于将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);

Both return: 0 if OK, 1 on error

这两个函数产生三种不同优先级的消息:普通、优先级波段和高优先级。

 

三、STREAMS ioctl操作

isastream函数:判断描述符是否引用一个流。

#include <stropts.h>

int isastream(int filedes);

Returns: 1 (true) if STREAMS device, 0 (false) otherwise

它通常是一个只对STREAMS设备才有效的ioctl函数进行测试的。

示例1:用ioctl实现isastream函数:测试描述符是否引用STREAM设备。

#include   <stropts.h>
#include   <unistd.h>
 
int isastream(int fd)
{
    return(ioctl(fd, I_CANPUT, 0) != -1);
}

 

示例2:测试isastream函数

#include <stropts.h>

#include <unistd.h>

#include <stdio.h>

#include <fcntl.h>

 

int main(int argc, char *argv[])

{

   int    i, fd;

 

   for (i = 1; i < argc; i++) {

       if ((fd = open(argv[i], O_RDONLY)) < 0) {

           printf("%s: can't open/n", argv[i]);

           continue;

       }

 

       if (isastream(fd) == 0)

           printf("%s: not a stream/n", argv[i]);

       else

           printf("%s: streams device/n", argv[i]);

    }

 

    exit(0);

}

[root@localhost chapter_14]# ./isastream /dev/tty /dev/null

/dev/tty: not a stream

/dev/null: not a stream

 

如果ioctl的参数requestI_LIST,则系统返回已压入该流所有模块的名字,包括最顶端的驱动程序。其第三个参数应当是指向str_list结构的指针。

struct str_list {
     int                sl_nmods;   /* number of entries in array */
     struct str_mlist  *sl_modlist; /* ptr to first element of array */
   };
struct str_mlist {
     char l_name[FMNAMESZ+1]; /* null terminated module name */
   };

FMNAMESZ在头文件<sys/conf.h>中定义,其值常常是8l_name的实际长度是FMNAMESZ+1,增加一个字节,是为了存放null终止符。

 

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

 

示例:列出流中的模块名

#include <unistd.h>

#include <stdio.h>

#include <fcntl.h>

#include <stropts.h>

#include <stdlib.h>

//#include <sys/conf.h>

 

#define FMNAMESZ 8

 

int main(int argc, char *argv[])

{

   int                fd, i, nmods;

   struct str_list    list;

   struct str_mlist m;

 

   if (argc != 2)

        {printf("usage: %s <pathname>/n", argv[0]);

        exit(1);}

 

   if ((fd = open(argv[1], O_RDONLY)) < 0)

        { printf("can't open %s/n", argv[1]);

        exit(2);

        }

 

   if (isastream(fd) == 0)

        { printf("%s is not a stream/n", argv[1]);

        exit(3);

        }

 

   /*

    * Fetch number of modules.

    */

   if ((nmods = ioctl(fd, I_LIST, (void *) 0)) < 0)

        {printf("I_LIST error for nmods/n");

        exit(4);

        }

   printf("#modules = %d/n", nmods);

 

   /*

    * Allocate storage for all the module names.

    */

   list.sl_modlist = calloc(nmods, sizeof(struct str_mlist));

   if (list.sl_modlist == NULL)

        {printf("calloc error/n");

        exit(5);

        }

 

   list.sl_nmods = nmods;

 

   /*

    * Fetch the module names.

    */

   if (ioctl(fd, I_LIST, &list) < 0)

        { printf("I_LIST error for list/n");

        exit(6);

        }

 

   /*

    * Print the names.

    */

   for (i = 1; i <= nmods; i++)

       printf(" %s: %s/n", (i == nmods) ? "driver" : "module",

         list.sl_modlist++->l_name);

 

   exit(0);

}

 

$ ./a.out /dev/console
     #modules = 5
     module: redirmod
     module: ttcompat
     module: ldterm
     module: ptem
     driver: pts

在我的CentOS上测试,console不是流设备而终止,后续测试,发现应该是本身系统就不支持STREAM

 

四、写(write)STREAMS设备

考虑两种情况:

1.如果写的数据超过最大值,则流首将这一个数据按最大长度分解成若干数据包。最后一个数据包的长度可能不到最大值。

2.如果像流写0个字节,除非流引用管道和FIFO,否则就顺流发送0长度消息。

 

五、写模式

可以用两个ioctl命令取得和设置一个流的写模式。如果将request设置为I_GWROPT,第三个参数设置为指向整型变量的指针,则该流的当前写模式在该整型量中返回。如果将request设置为I_SWROPT,第三个参数是一个整型值,则其值成为该流的新的写模式。

目前,流的写模式只定义了两个:

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

SNDPIPE 在流上已出错后,若调用writeputmsg,则向调用进程发送SIGPIPE信号。

 

六、读模式

1.用于流读的两个函数getmsggetpmsg

#include <stropts.h>

int getmsg(int filedes, struct strbuf *restrictctlptr,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);

Both return: non-negative value if OK, 1 on error

2.读模式

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

读模式可由下面三个常量指定:

RNORM普通,字节流模式。

RMSGN消息不丢弃模式。

RMSGD消息丢弃模式。

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

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

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

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

 

任何时刻,只能设置一种消息读模式以及一种协议读模式。默认模式是(RNORM|RPROTNORM).

#include <errno.h>

#include <fcntl.h>

#include <sys/wait.h>

#include <sys/stat.h>

#include <unistd.h>

#include <stdio.h>

#include <stropts.h>

#include <stdlib.h>

 

#define BUFFSIZE    4096

 

int

main(void)

{

   int            n, flag;

   char           ctlbuf[BUFFSIZE], datbuf[BUFFSIZE];

   struct strbuf  ctl, dat;

 

   ctl.buf = ctlbuf;

   ctl.maxlen = BUFFSIZE;

   dat.buf = datbuf;

   dat.maxlen = BUFFSIZE;

   for ( ; ; ) {

       flag = 0;      /* return any message */

       if ((n = getmsg(STDIN_FILENO, &ctl, &dat, &flag)) < 0)

           printf("getmsg error/n");

       fprintf(stderr, "flag = %d, ctl.len = %d, dat.len = %d/n",

         flag, ctl.len, dat.len);

       if (dat.len == 0)

           exit(0);

       else if (dat.len > 0)

           if (write(STDOUT_FILENO, dat.buf, dat.len) != dat.len)

               printf("write error/n");

   }

}

 


版权声明:本文为yylklshmyt20090217原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。