美文网首页LinuxLinux学习之路
APUE读书笔记-18终端输入输出(13)

APUE读书笔记-18终端输入输出(13)

作者: QuietHeart | 来源:发表于2020-09-06 22:21 被阅读0次

11、noncanonical模式

Noncanonical模式通过关闭termios结构中的c_lflag域中的ICANON标记来指定。在noncanonical模式,输入的数据不会被收集成行,并且这些字符不会被处理:ERASE, KILL, EOF, NL, EOL, EOL2, CR, REPRINT, STATUS, WERASE。

如我们所说,canonical模式比较简单,系统每次返回一行。但是noncanonical模式中,系统如何知道什么时候给我们返回数据?如果每次返回一个字节,那么开销就会很大。(前面给出过我们一次read一个字节的开销,每次我们将返回的数据量增倍,相应的系统调用的开销就会减半)。系统不能总是每次返回多个字节,因为有时候我们直到我们开始读取之前,我们都无法知道需要读取多少数据。

有一个解决的方法就是,当已经读取了指定数目的数据或者过了一个指定的时间之后,告诉系统返回。这个技术使用两个变量,它们存放在termios结构中的c_cc数组中:MIN和TIME。这两个数组的元素通过名称VMIN和VTIME被索引到。

MIN指定了一个read返回之前的最小字节数目。TIME指定了等待数据到达的每个10分之一秒的数目。主要有4种情况:

  • Case A: MIN > 0, TIME > 0

    TIME指定了一个交互字节计时器,这个计时器只有在第一个字节被接收到的时候会被启动。如果在计时器超时之前,MIN字节的数据被读取到了,那么read会返回MIN字节。如果计时器在接收到MIN个字节之前超时了,那么read会返回当前已经读取的字节数目。(如果超时了,那至少会返回1个字节,因为计时器在接收到一个字节的时候才会被启动)这个情况下,调用者会阻塞,直到收到第一个字节,如果调用read的时候数据已经可用,那么就好象read之后数据立即被接收到了。

  • Case B: MIN > 0, TIME == 0

    read不会返回,直到MIN字节被接收到。这会导致read处于一种永远阻塞的状态。

  • Case C: MIN == 0, TIME > 0

    TIME指定了一个读取计时器,这个计时器会在read被调用的时候启动。(和case A进行比较会发现,case A中的非零TIME表示一个交互字节计时器,这个计时器只有接收到第一个字节的时候才会被启动),read会在接收到一个单个字节的时候返回,或者计时器超时的时候返回。如果计时器超时了,那么read返回0。

  • Case D: MIN = 0, TIME = 0

    如果一些数据是可用的,那么read返回请求的字节的数目。如果没有数据可用,那么read会立即返回0。

需要注意的是,对于以上这些情况,MIN只是一个最小值。如果应用程序请求的数据大于MIN字节,那么会可能接收到所有可能请求的数据。这也是C和D中的情况,即C和D中的MIN都是0。

下面的图表列出了noncanonical输入下的四种情况。这个图中,nbytes就是read的第3个参数(也就是返回的最大字节数目)。

noncanonical输入模式中的四种情况

                    MIN > 0                           MIN == 0
          +------------------------------------+------------------------------+
          | A:read returns [MIN,nbytes]        | C:read returns [1,nbytes]    |
          |    before timer expires;           |       before timer expires;  |
TIME > 0  |   read returns [1,MIN)             |   read returns 0             |
          |    if timer expires.               |       if timer expires.      |
          | (TIME= interbyte timer             | (TIME= read timer.)          |
          |  Caller can block indefinitely.)   |                              |
          +------------------------------------+------------------------------+
          | B:read returns [MIN,nbytes]        | D:read returns [0,nbytes]    |
TIME == 0 |      when available.               |       immediately.           |
          | (Caller can block indefinitely.)   |                              |
          +------------------------------------+------------------------------+

我们需要注意的是POSIX.1允许下标VMIN和VTIME分别和VEOF与VEOL的值一样。其实,Solaris为了兼容以前的老版本的System V,就是这样做的。这导致了一个移植的问题。当从noncanonical转换到canonical模式的时候,我们现在必须也恢复VEOF和VEOL,如果VMIN和VEOF一样,并且我们没有恢复它们的值,那么当我们设置VMIN为经常使用的1的时候,end-of-file字符就变成了Control-A。回避这个问题的最简单的方法就是当进入到noncanonical模式的时候保存整个termios结构,回到canonical模式的时候恢复它。

例子

下面的程序定义了tty_cbreak和tty_raw函数,这函数设置终端为cbreak模式和raw模式(cbreak和raw来自终端驱动的版本7)。我们可以调用函数tty_reset来重新设置终端为它的原始模式(也就是调用这些函数之前的状态)。

相关文章

网友评论

    本文标题:APUE读书笔记-18终端输入输出(13)

    本文链接:https://www.haomeiwen.com/subject/qymnjktx.html