⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 12.htm

📁 操作系统方面的书籍
💻 HTM
📖 第 1 页 / 共 5 页
字号:

<p>有时需要确定一个描述符是否引用一个流。这与调用isatty函数来确定一个描述符 
</p>

<p>是否引用一个终端设备相类似(11.9节)。SVR4提供isastream函数。 </p>

<p>int isastream(int filedes); </p>

<p>返回:若是流返回1,否则返回0 </p>

<p>(由于某种原因,SVR4的设计者忘记将此函数的原型放在头文件中,所以我们也不 
</p>

<p>能为此函数写一条#include语句。) </p>

<p>与isatty类似,它通常是用一个只对流设备才有效的ioctl函数来进行测试的 
</p>

<p>。程序12.8是该函数的一种可能的实现。它使用I_CANPUT ioctl来测试由第3个参 
</p>

<p>数说明的优先波段是否是可写的?如果该ioctl执行成功,则它对所涉及的流并末 
</p>

<p>作任何改变。 </p>

<p>#include &lt;stropts.h&gt; </p>

<p>#include &lt;unistd.h&gt; </p>

<p>int </p>

<p>isastream(int fd) </p>

<p>{ </p>

<p>return(ioctl(fd, I_CANPUT, 0) != -1); </p>

<p>} </p>

<p>程序12.8 检查描述符是否引用流设备 </p>

<p>程序12.9 可用于测试此函数。 </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;sys/fcntl.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>int </p>

<p>main(int argc, char *argv[]) </p>

<p>{ </p>

<p>int i, fd; </p>

<p>for (i = 1; i &lt; argc; i++) { </p>

<p>printf(&quot;%s: &quot;, argv[i]); </p>

<p>if ( (fd = open(argv[i], O_RDONLY)) &lt; 0) { </p>

<p>err_ret(&quot;%s: can't open&quot;, argv[i]); </p>

<p>continue; </p>

<p>} </p>

<p>if (isastream(fd) == 0) </p>

<p>err_ret(&quot;%s: not a stream&quot;, argv[i]); </p>

<p>else </p>

<p>err_msg(&quot;%s: streams device&quot;, argv[i]); </p>

<p>} </p>

<p>exit(0); </p>

<p>} </p>

<p>程序12.9 测试isasteam函数 </p>

<p>运行此程序,得到很多由ioctl函数返回的出错信息。 </p>

<p>$ a.out /dev/tty /dev/vidadm /dev/null /etc/motd </p>

<p>/dev/tty:/dev/tty:streams device </p>

<p>/dev/vidadm:/dev/vidadm:not a stream:Invalid argument </p>

<p>/dev/null:/dev/null:not a stream:No suck device </p>

<p>/etc/motd: /etc/motd:not a stream:Not a typewriter </p>

<p>/dev/tty在SVR之下是个流设备,这与我们所期望的一致。/dev/vidadm不是一个流 
</p>

<p>设备,但是它是支持其它ioctl请求的字符特殊文件。对于不知道这种ioctl请求的 
</p>

<p>设备,它返回EINVAL。/dev/null是一种不支持任何ioctl操作的字符特殊文件,所 
</p>

<p>以ioctl返回ENODEV。最后,/etc/motd是一个普通文件,而不是字符特殊文件,所 
</p>

<p>以返回ENOTTY(这是在这种情况下的典型返回值)。 </p>

<p>ENOTTY(&quot;不是打字机&quot;)是个历史产物,当ioctl企图对并不引用字符特 
</p>

<p>殊设备 </p>

<p>的描述符进行操作时,Unix系统核都返回ENOTTY。 </p>

<p>实例 </p>

<p>如果ioctl参数request是I_LIST,则系统返回该流上所有模块的名字,包括最顶端 
</p>

<p>的驱动程序。其第3个参数应当是指向str_list结构是指针。 </p>

<p>struct str_list{ </p>

<p>int sl_nmods; 数组中项的数目 </p>

<p>struct str_modlist *sl_modlist; 指向数组中第一个元素的指针 </p>

<p>}; </p>

<p>应将sl_modlist设置为指向str_mlist结构数组的第一个元素,将sl_nmods设置为 
</p>

<p>该数组中的项数。 </p>

<p>struct str_mlist { </p>

<p>char l_name[FMNAMESZ+1]; 以null结尾的模块名字 </p>

<p>}; </p>

<p>常数FMNAMESZ定义在头文件&lt;sys/conf.h&gt;中,其值常常是8。l_name的实际长度是 
</p>

<p>FMNAMESZ+1,增加1个字节是为了存放null终止符。 </p>

<p>如果ioctl的第3个参数是0,则该函数返回的是模块数,而不是模块名。我们将先 
</p>

<p>用这种ioctl调用确定模块数,然后再分配所要求的str_mlist结构数。 </p>

<p>程序12.10例示了I_LIST操作。由ioctl返回的名字列表并不对模块和驱动程序进行 
</p>

<p>区分,因为在该列表中的最后一项是处于流底部的驱动程序,所以在打印时将其标 
</p>

<p>明为驱动程序。 </p>

<p>#include &lt;sys/conf.h&gt; </p>

<p>#include &lt;sys/types.h&gt; </p>

<p>#include &lt;fcntl.h&gt; </p>

<p>#include &lt;stropts.h&gt; </p>

<p>#include &quot;ourhdr.h&quot; </p>

<p>int </p>

<p>main(int argc, char *argv[]) </p>

<p>{ </p>

<p>int fd, i, nmods; </p>

<p>struct str_list list; </p>

<p>if (argc != 2) </p>

<p>err_quit(&quot;usage: a.out &lt;pathname&gt;&quot;); </p>

<p>if ( (fd = open(argv[1], O_RDONLY)) &lt; 0) </p>

<p>err_sys(&quot;can't open %s&quot;, argv[1]); </p>

<p>if (isastream(fd) == 0) </p>

<p>err_quit(&quot;%s is not a stream&quot;, argv[1]); </p>

<p>/* fetch number of modules */ </p>

<p>if ( (nmods = ioctl(fd, I_LIST, (void *) 0)) &lt; 0) </p>

<p>err_sys(&quot;I_LIST error for nmods&quot;); </p>

<p>printf(&quot;#modules = %d\n&quot;, nmods); </p>

<p>/* allocate storage for all the module names */ </p>

<p>list.sl_modlist = calloc(nmods, sizeof(struct str_mlist)); </p>

<p>if (list.sl_modlist == NULL) </p>

<p>err_sys(&quot;calloc error&quot;); </p>

<p>list.sl_nmods = nmods; </p>

<p>/* and fetch the module names */ </p>

<p>if (ioctl(fd, I_LIST, &amp;list) &lt; 0) </p>

<p>err_sys(&quot;I_LIST error for list&quot;); </p>

<p>/* print the module names */ </p>

<p>for (i = 1; i &lt;= nmods; i++) </p>

<p>printf(&quot; %s: %s\n&quot;, (i == nmods) ? &quot;driver&quot; : &quot;module&quot;, </p>

<p>list.sl_modlist++); </p>

<p>exit(0); </p>

<p>} </p>

<p>程序12.10 打印流中的模块名 </p>

<p>$ who </p>

<p>stevens console sep 25 06:12 </p>

<p>stevens pts001 Oct 12 07:12 </p>

<p>$ a.out /dev/pts001 </p>

<p>#modules=4 </p>

<p>module:ttcompat </p>

<p>module:ldterm </p>

<p>module:ptem </p>

<p>driver:pts </p>

<p>$ a.out /dev/console </p>

<p>#modules=5 </p>

<p>module:ttcompat </p>

<p>module:ldterm </p>

<p>module:ansi </p>

<p>module:char </p>

<p>driver:cmux </p>

<p>在这两种情形中,顶上的两个流模块都是一样的(ttcompac和ldterm),但是余下 
</p>

<p>的模块和驱动程序则不同。在第十九章中我们将说明伪终端情形。 
</p>

<p>write至流设备 </p>

<p>在图12.10中可以看到write至流设备产生一个M_DATA消息。一般而言,这确实如此 
</p>

<p>,但是也还有一些情况需要考虑。首先,流中顶部的一个处理模块规定了可顺流传 
</p>

<p>送的最小、最大数据包长度。(无法查询该模块中规定的这些值。)如果write的 
</p>

<p>数据长度超过最大值,则流首将这一数据分解成最大长度的若干数据包。最后一个 
</p>

<p>数据包的长度则小于最大值。 </p>

<p>接着要考虑的是:如果向流write 0个字节,又将如何呢?除非流涉及管道或FIFO 
</p>

<p>,否则就顺流发送0长消息。对于管道FIFO,为与以前版本兼容,系统的默认处理 
</p>

<p>方式是忽略0长write。可以用ioctl设置实现管道和FIFO的流写方式以更改这种默 
</p>

<p>认处理方式。 </p>

<p>写方式 </p>

<p>可以用ioctl取得和设置一个流的写方式。如果将request设置为I_GWROPT,第三个 
</p>

<p>参数为指向一个整型变量的指针,则该流的当前写方式就在该整型量中返回。如果 
</p>

<p>将request设置为I_SWROPT,第3个参数是一个整型,则其值就成为该流新的写方式。 
</p>

<p>如同处理文件描述符标志和文件状态标志(3.13节)一样,总是应当先取当前写方式 
</p>

<p>值,然后修改它,而不只是将写方式设置为某个绝对值(很可能要关闭某些原来打开 
</p>

<p>的位)。 </p>

<p>目前,只定义了两个写方式值。 </p>

<p>SNDZERO 对管道和FIFO的0长写会造成顺流传送一个0长消息。按系统默认,0长写 
</p>

<p>不发送消息。 </p>

<p>SNPIPE 在流上已出错后,若调用write或putmsg,则向调用进程发送SIGPIPE信 
</p>

<p>号。 </p>

<p>一个流也有读方式,我们先说明getmsg和getpmsg函数,然后再说明读方式。 
</p>

<p>_______________________________________________________________________ </p>

<p>____ </p>

<p>#include &lt;stropts.h&gt; </p>

<p>int getmsg(int filedes,struct strbuf *ctlptr, </p>

<p>struct strbuf *dataptr,int *flagptr); </p>

<p>int getpmsg(int filedes,struct strbuf *ctlptr, </p>

<p>struct strbuf *dataptr, int *bandptr, int *flagptr); </p>

<p>两个函数返回:若成功为非负值,出错为-1 </p>

<p>_____________________________________________________________________ </p>

<p>________ </p>

<p>getmsg和getpmsg函数 </p>

<p>用read,getmsg和getpmsg从流首读流消息。 </p>

<p>注意,flagptr和bandptr是指向整型的指针。在调用之前,这两个指针所指向的整 
</p>

<p>型单元中应设置成所希望的消息类型,在返回时,此整型量设置为所读到的消息的 
</p>

<p>类型。 </p>

<p>如果flagptr指向的整型单元的值是0,则getmsg返回在流首读队列中的下一个消息 
</p>

<p>。如果下一个消息是高优先权消息,则在返回时,flagptr所指向的整型单元设置 
</p>

<p>为RS_HIPRI。如果希望只接收高优先权消息,则在调用getmsg之前必须将flagptr 
</p>

<p>所指向的整型单元设置为RS_HIPRI。 </p>

<p>这两个函数有很多条件来确定返回给调用者何种消息:(a)flagptr和bandptr所指 
</p>

<p>向的值,(b)ctlptr-&gt;maxlen和dataptr-&gt;maxlen的值。对使用getmsg而言,并不需 
</p>

<p>要了解所有这些细节。如欲了解这些细节请参阅getmsg(2)手册页。 
</p>

<p>读方式 </p>

<p>如果read流设备会发生些什么呢?有两个潜在的问题:(1)如果读到流中消息的记 
</p>

<p>录边界将会怎样?(2)如果调用read,而流中下一个消息有控制信息又将如何?对 
</p>

<p>第一种情况的默认处理方式被称为字节流方式。在这种方式中,read从流中取数据 
</p>

<p>直至满足了要求,或者已经没有数据。在这种方式中,忽略流中消息的边界。对第 
</p>

<p>二种情况的默认处理是,如果在队列的前端有控制消息,则read出错返回。可以改 
</p>

<p>变这两种默认处理方式。 </p>

<p>在调用ioctl时,若将request设置为I_GRDOPT,第3个参数又是指向一个整型单元 
</p>

<p>的指针,则对该流的当前读方式在该整型单元中返回。如果将request设置为I_SR 
</p>

<p>DOPT,第3个参数是整型值,则该流的读方式设置为该值。读方式值有下列三个: 
</p>

<p>RNORM 普通,字节流方式(与上面说明的相同)。这是默认方式。 </p>

<p>RMSGN 
消息不删除方式。读从流中取数据直至读到所要求的字节数,或者到达消 
</p>

<p>息边界。如果某次读只用了一个消息的一部分,则其余下部分仍留在流中,以供下 
</p>

<p>一个读取。 </p>

<p>RMSGD 
消息删除方式。这与不删除方式的区别是,如果某次读只用一个消息的一 
</p>

<p>部分。则余下部分就被删除,不再使用。 </p>

<p>在读方式中还可指定另外三个常数,以便设置在读到流中包含协议信息的消息时r 
</p>

<p>ead的处理方法: </p>

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

<p>。 </p>

<p>RPROTDAT 协议-数据方式:read将控制部分作为数据返回给调用者。 </p>

<p>RPROTDIS 协议-删除方式:read删除消息中的控制信息,但是返回消息中的数 
</p>

<p>据。 </p>

<p>实例 </p>

<p>程序12.11是在程序3.3的基础上改写

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -