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

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

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

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


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

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


任意数量的处理模块可以压入流,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的不同参数锁产生的不同消息类型。


控制信息和数据由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)。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
图书目录 第1章 UNIX基础知识 1 1.1 引言 1 1.2 UNIX体系结构 1 1.3 登录 1 1.4 文件和目录 3 1.5 输入和输出 6 1.6 程序和进程 8 1.7 出错处理 10 1.8 用户标识 12 1.9 信号 14 1.10 时间值 15 1.11 系统调用和库函数 16 1.12 小结 17 习题 18 第2章 UNIX标准化及实现 19 2.1 引言 19 2.2 UNIX标准化 19 2.2.1 ISO C 19 2.2.2 IEEE POSIX 20 2.2.3 Single UNIX Specification 25 2.2.4 FIPS 26 2.3 UNIX系统实现 26 2.3.1 SVR4 26 2.3.2 4.4BSD 27 2.3.3 FreeBSD 27 2.3.4 Linux 27 2.3.5 Mac OS X 28 2.3.6 Solaris 28 2.3.7 其他UNIX系统 28 2.4 标准和实现的关系 28 2.5 限制 29 2.5.1 ISO C限制 29 2.5.2 POSIX限制 30 2.5.3 XSI限制 32 2.5.4 sysconf、pathconf和fpathconf函数 32 2.5.5 不确定的运行时限制 38 2.6 选项 42 2.7 功能测试宏 44 2.8 基本系统数据类型 45 2.9 标准之间的冲突 45 2.10 小结 46 习题 46 第3章 文件I/O 47 3.1 引言 47 3.2 文件描述符 47 3.3 open函数 48 3.4 creat函数 49 3.5 close函数 50 3.6 lseek函数 50 3.7 read函数 53 3.8 write函数 54 3.9 I/O的效率 54 3.10 文件共享 56 3.11 原子操作 59 3.12 dup和dup2函数 60 3.13 sync、fsync和fdatasync函数 61 3.14 fcntl函数 62 3.15 ioctl函数 66 3.16 /dev/fd 67 3.17 小结 68 习题 68 第4章 文件和目录 71 4.1 引言 71 4.2 stat、fstat和lstat函数 71 4.3 文件类型 72 4.4 设置用户ID和设置组ID 74 4.5 文件访问权限 75 4.6 新文件和目录的所有权 77 4.7 access函数 77 4.8 umask函数 79 4.9 chmod和fchmod函数 81 4.10 粘住位 83 4.11 chown、fchown和lchown函数 84 4.12 文件长度 85 4.13 文件截短 86 4.14 文件系统 86 4.15 link、unlink、remove和rename函数 89 4.16 符号链接 91 4.17 symlink和readlink函数 94 4.18 文件的时间 94 4.19 utime函数 95 4.20 mkdir和rmdir函数 97 4.21 读目录 98 4.22 chdir、fchdir和getcwd函数 102 4.23 设备特殊文件 104 4.24 文件访问权限位小结 106 4.25 小结 106 习题 107 第5章 标准I/O库 109 5.1 引言 109 5.2 流和FILE对象 109 5.3 标准输入、标准输出和标准出错 110 5.4 缓冲 110 5.5 打开流 112 5.6 读和写流 114 5.7 每次一行I/O 116 5.8 标准I/O的效率 117 5.9 二进制I/O 119 5.10 定位流 120 5.11 格式化I/O 121 5.12 实现细节 125 5.13 临时文件 127 5.14 标准I/O的替代软件 130 5.15 小结 130 习题 130 第6章 系统数据文件和信息 133 6.1 引言 133 6.2 口令文件 133 6.3 阴影口令 136 6.4 组文件 137 6.5 附加组ID 138 6.6 实现的区别 139 6.7 其他数据文件 139 6.8 登录账户记录 140 6.9 系统标识 141 6.10 时间和日期例程 142 6.11 小结 146 习题 146 第7章 进程环境 147 7.1 引言 147 7.2 main函数 147 7.3 进程终止 147 7.4 命令行参数 151 7.5 环境表 152 7.6 C程序的存储空间布局 152 7.7 共享库 154 7.8 存储器分配 154 7.9 环境变量 157 7.10 setjmp和longjmp函数 159 7.11 getrlimit和setrlimit函数 164 7.12 小结 168 习题 168 第8章 进程控制 171 8.1 引言 171 8.2 进程标识符 171 8.3 fork函数 172 8.4 vfork函数 176 8.5 exit函数 178 8.6 wait和waitpid函数 179 8.7 waitid函数 183 8.8 wait3和wait4函数 184 8.9 竞争条件 185 8.10 exec函数 188 8.11 更改用户ID和组ID 192 8.12 解释器文件 196 8.13 system函数 200 8.14 进程会计 203 8.15 用户标识 208 8.16 进程时间 208 8.17 小结 210 习题 211 第9章 进程关系 213 9.1 引言 213 9.2 终端登录 213 9.3 网络登录 216 9.4 进程组 218 9.5 会话 219 9.6 控制终端 220 9.7 tcgetpgrp、tcsetpgrp和tcgetsid函数 221 9.8 作业控制 222 9.9 shell执行程序 225 9.10 孤儿进程组 228 9.11 FreeBSD实现 230 9.12 小结 231 习题 232 第10章 信号 233 10.1 引言 233 10.2 信号概念 233 10.3 signal函数 240 10.4 不可靠的信号 242 10.5 中断的系统调用 244 10.6 可重入函数 246 10.7 SIGCLD语义 248 10.8 可靠信号术语和语义 250 10.9 kill和raise函数 251 10.10 alarm和pause函数 252 10.11 信号集 256 10.12 sigprocmask函数 258 10.13 sigpending函数 259 10.14 sigaction函数 261 10.15 sigsetjmp和siglongjmp函数 266 10.16 sigsuspend函数 268 10.17 abort函数 274 10.18 system函数 276 10.19 sleep函数 280 10.20 作业控制信号 282 10.21 其他特征 284 10.22 小结 285 习题 285 第11章 线程 287 11.1 引言 287 11.2 线程概念 287 11.3 线程标识 288 11.4 线程的创建 288 11.5 线程终止 291 11.6 线程同步 297 11.7 小结 311 习题 311 第12章 线程控制 313 12.1 引言 313 12.2 线程限制 313 12.3 线程属性 314 12.4 同步属性 318 12.5 重入 324 12.6 线程私有数据 328 12.7 取消选项 331 12.8 线程和信号 333 12.9 线程和fork 336 12.10 线程和I/O 339 12.11 小结 340 习题 340 第13章 守护进程 341 13.1 引言 341 13.2 守护进程的特征 341 13.3 编程规则 342 13.4 出错记录 345 13.5 单实例守护进程 348 13.6 守护进程的惯例 350 13.7 客户进程-服务器进程模型 354 13.8 小结 354 习题 354 第14章 高级I/O 355 14.1 引言 355 14.2 非阻塞I/O 355 14.3 记录锁 357 14.4 STREAMS 370 14.5 I/O多路转接 379 14.5.1 select和pselect函数 381 14.5.2 poll函数 384 14.6 异步I/O 386 14.6.1 系统V异步I/O 386 14.6.2 BSD异步I/O 387 14.7 readv和writev函数 387 14.8 readn和writen函数 389 14.9 存储映射I/O 390 14.10 小结 395 习题 396 第15章 进程间通信 397 15.1 引言 397 15.2 管道 398 15.3 popen和pclose函数 403 15.4 协同进程 408 15.5 FIFO 412 15.6 XSI IPC 415 15.6.1 标识符和键 415 15.6.2 权限结构 416 15.6.3 结构限制 417 15.6.4 优点和缺点 417 15.7 消息队列 418 15.8 信号量 422 15.9 共享存储 427 15.10 客户进程-服务器进程属性 432 15.11 小结 434 习题 434 第16章 网络IPC:套接字 437 16.1 引言 437 16.2 套接字描述符 437 16.3 寻址 439 16.3.1 字节序 440 16.3.2 地址格式 441 16.3.3 地址查询 442 16.3.4 将套接字与地址绑定 449 16.4 建立连接 450 16.5 数据传输 452 16.6 套接字选项 464 16.7 带外数据 466 16.8 非阻塞和异步I/O 467 16.9 小结 468 习题 468 第17章 高级进程间通信 469 17.1 引言 469 17.2 基于STREAMS的管道 469 17.2.1 命名的STREAMS管道 472 17.2.2 唯一连接 473 17.3 UNIX域套接字 476 17.3.1 命名UNIX域套接字 477 17.3.2 唯一连接 478 17.4 传送文件描述符 482 17.4.1 经由基于STREAMS的管道传送文件描述符 484 17.4.2 经由UNIX域套接字传送文件描述符 486 17.5 open服务器版本1 493 17.6 open服务器版本2 498 17.7 小结 505 习题 505 第18章 终端I/O 507 18.1 引言 507 18.2 综述 507 18.3 特殊输入字符 512 18.4 获得和设置终端属性 516 18.5 终端选项标志 516 18.6 stty命令 522 18.7 波特率函数 523 18.8 行控制函数 524 18.9 终端标识 524 18.10 规范模式 529 18.11 非规范模式 532 18.12 终端的窗口大小 537 18.13 termcap,terminfo和curses 539 18.14 小结 540 习题 540 第19章 伪终端 541 19.1 引言 541 19.2 概述 541 19.3 打开伪终端设备 544 19.3.1 基于STREAMS的伪终端 547 19.3.2 基于BSD的伪终端 549 19.3.3 基于Linux的伪终端 551 19.4 pty_fork函数 553 19.5 pty程序 555 19.6 使用pty程序 559 19.7 高级特性 564 19.8 小结 565 习题 565 第20章 数据库函数库 567 20.1 引言 567 20.2 历史 567 20.3 函数库 568 20.4 实现概述 569 20.5 集中式或非集中式 572 20.6 并发 574 20.7 构造函数库 574 20.8 源代码 575 20.9 性能 598 20.10 小结 600 习题 601 第21章 与网络打印机通信 603 21.1 引言 603 21.2 网络打印协议 603 21.3 超文本传输协议 605 21.4 打印假脱机技术 605 21.5 源代码 607 21.6 小结 644 习题 645 附录A 函数原型 647 附录B 其他源代码 677 附录C 部分习题答案 685 参考书目 709 索引 715

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值