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 *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);

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");

    }

}

 

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值