分类: LINUX
2012-02-28 20:34:55
++++++APUE读书笔记-18终端输入输出-03特殊输入字符++++++
3、特殊输入字符
================================================
POSIX.1定义了输入时候需要处理的11个特殊字符,有些实现定义了额外的字符,请参考参考资料中的表,这里就不列举了。后面我们会对它们依次进行描述。
(这些特殊符号,例如回车,换行,退格等,通过在终端键入特殊按键来输入,例如^H,其实是[Control]H)
对于11个POSIX.1特殊字符,我们可以将其中的9个修改成我们喜欢的任何值。换行符号和回车符号是不能改变的(分别是\n和\r),并且可能STOP和START字符也可能不能改变(取决与具体实现)。我们通过修改termios结构中的c_cc数组成员中的特定元素来实现这个目的。这个数组成员中的元素通过一个名称被索引到,而这个名称以V开头,本书上节参考资料前面列出的表中的第三列中的元素就是。
POSIX.1允许我们禁止这些字符。如果我们设置c_cc数组中的一个元素值为_POSIX_VDISABLE,那么我们就相当与将相应的特殊符号给禁止了。
早期版本的Single UNIX Specification对_POSIX_VDISABLE的支持是可选的,而现在它已经变成了需要的了。
本文中的所有四种平台都支持这个特性。Linux 2.4.22和Solaris 9 将_POSIX_VDISABLE定义为0,FreeBSD 5.2.1和Mac OS X 10.3将它定义成0xff。
有些早期的UNIX系统会在相应的特殊输入字符为0的时候,禁止某个特性。
举例
在我们详细描述所有的特殊字符的时候,我们来看一下一个修改它们的小程序。下面代码中的程序禁止了中断字符,并且设置文件结束字符为Control-B。
禁止中断和改变文件结束符号的小程序
#include "apue.h"
#include
int main(void)
{
struct termios term;
long vdisable;
if (isatty(STDIN_FILENO) == 0)
err_quit("standard input is not a terminal device");
if ((vdisable = fpathconf(STDIN_FILENO, _PC_VDISABLE)) < 0)
err_quit("fpathconf error or _POSIX_VDISABLE not in effect");
if (tcgetattr(STDIN_FILENO, &term) < 0) /* fetch tty state */
err_sys("tcgetattr error");
term.c_cc[VINTR] = vdisable; /* disable INTR character */
term.c_cc[VEOF] = 2; /* EOF is Control-B */
if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0)
err_sys("tcsetattr error");
exit(0);
}
对于这个程序,我们注意如下几点:
a)我们只有在标准输入是终端设备的时候,才会修改终端字符。我们调用isatty函数来对此进行检测。
b)我们使用fpathconf函数来获得_POSIX_VDISABLE值。
c)函数tcgetattr从内核获取一个termios结构。在我们修改了这个结构之后,我们调用tcsetattr函数来设置相应属性。所变化的属性只是我们特别修改的那些属性。
d)禁止中断键和忽略中断信号不一样。上面代码中的程序只是禁止了导致终端驱动产生SIGINT信号的特殊字符,我们还是可以通过kill函数来给进程发送这个SIGINT信号的。
下面我们具体讨论每个特殊的字符。我们把这些字符称作特殊输入字符,但是有两个字符,STOP和START(Control-S和Control-Q),也会在输出的时候被特殊处理。注意,当这些特殊字符被终端驱动认出并且进行特殊处理之后,这些字符然后就会被丢弃:也就是说,他们不会在read操作中返回给进程。对于这个的一个例外就是,换行符号(NL,EOL,EOL2)和回车符号(CR)不这样。
CR
回车(carriage return)字符。我们无法改变这个字符,这个字符在通常(canonical)输入模式中被识别。当ICANON(canonical模式)和ICRNL(将CR映射成NL)被设置,并且IGNCR(忽略CR)没有被设置的时候,(它们都是前面的c_iflag标记)CR字符就被转换成NL字符,并且和NL字符具有一样的功能。这个字符(可能被转换成NL之后)被返回给读取进程。
DISCARD
丢弃(discard)字符。这个字符,在extended(IEXTEN)输入模式中被识别,导致接下来的输出被丢弃,直到输入另外一个DISCARD字符或者清除了丢弃条件(参见FLUSHO选项)。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
DSUSP
延迟挂起(delayed-suspend)作业控制(job-control)字符。如果支持作业控制并且ISIG标记(前面的c_lflag)被设置,这个字符在extended(IEXTEN)输入模式中被识别。如同SUSP字符,这个延迟挂起字符产生SIGSTP信号,发送给所有前台的进程组。但是延迟挂起字符只有在进程从控制终端读取的时候才产生信号,而不是字符被打入的时候。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
EOF
文件结束(end-of-file)字符。这个字符在canonical输入模式(ICANON)中被识别。当我们键入这个字符的时候,所有等待被读取的字节都会被立即被传递给读取进程。如果没有其他字节等待被读取,那么会返回0。在一行的开始输入EOF字符是一个告诉程序到达文件结束的常用的方法。这个字符在canonical模式之中被处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
EOL
额外的行定界字符。类似NL,这个字符在canonical输入模式(ICANON)中被识别,并且会被返回给读取的进程;然而这个字符一般来说都不会使用。
EOL2
另外的行定界字符。类似NL,这个字符和EOL字符处理的方式一样。
ERASE
擦除字符(backspace)。这个字符在canonical输入模式(ICANON)中被识别,并且擦除本行中前面的字符,不会擦除本行开始以前的字符。这个字符在canonical模式中处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
ERASE2
一个可选的擦除字符(backspace)。这个字符的处理方式和ERASE字符的处理方式一样。
INTR
中断字符。这个字符在ISIG标记被设置的时候会在输入的时候被识别出来(前面的c_lflag),并且产生SIGINT信号,发送给所有前台进程组的进程。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
KILL
kill字符。(名称kill已经被过度的使用了;记得前面我们发送信号给进程的时候就调用kill函数,而这里这个字符应该被称作行擦除字符,它和信号没有太大的关系)这个字符在canonical输入模式(ICANON)中被识别,它会擦除整行的字符,这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
LNEXT
literal-next字符。这个字符,在extended(IEXTEN)输入模式中被识别并且导致任何接下来的字符的特殊含义会忽略(我的理解是,输入这个字符之后,后面字符如果是特殊字符,那么它的特殊含义会被取消,当做正常字符了)。这个对于本节中我们所列出的所有的特殊字符都是管用的。我们使用这个字符就可以向应用程序键入任何字符。LNEXT字符会在处理之后被忽略,但是在它后面输入的特殊字符(包括LNEXT字符本身)会被传递给进程。
NL
新行(newline)字符,也被称作行定界符号。我们无法改变这个字符,这个字符在canonical输入模式(ICANON)中被识别,会被返回给读取的进程。
QUIT
退出(quit)字符。这个字符再ISIG标记被设置的时候会被识别。quit字符产生SIGQUIT信号,这个信号被发送给所有前台进程组的进程。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
记得之前我们说过INTR和QUIT的不同在于QUIT字符不仅仅终止进程,而且还产生core文件(就是包含了进程运行时候内存状态信息的文件)。
REPRINT
reprint字符。这个字符,在extended输入模式中,以及canonical输入模式中(IEXTEN和ICANON标记都被设置)被识别,并且导致所有没有读取的输入被输出(reechoed)。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
START
start字符。这个字符会在IXON标记被设置的时候在输入的时候被识别,并且在设置了IXOFF标记(c_iflag)的时候会自动产生为输出。设置IXON之后接收到START字符会导致(之前由于输入STOP字符)被停止的输出被重新开始。在这情况下,这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
当IXOFF被设置的时候,终端驱动自动产生一个START字符来重新开始之前被停止了的输入,当然,这是在新的输入不会覆盖输入缓存的时候。
STATUS
BSD的status-request字符。这个字符,在extended输入模式中,以及canonical输入模式中(IEXTEN和ICANON标记都被设置)被识别,并且产生SIGINFO信号,这个信号会被发送给所有前台的进程组的进程。另外,如果NOKERNINFO标记(c_lflag)没有被设置的时候,前台进程组的状态信息也会被显示到终端上面。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
STOP
stop字符。这个字符会在IXON标记被设置的时候在输入的时候被识别,并且在设置了IXOFF标记(c_iflag)的时候会自动产生为输出。在IXON被设置的时候,接收到STOP字符会停止输出。这个时候,这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。被停止的输出会在START字符被键入的时候重新开始。
当IXOFF被设置的时候,终端驱动会自动产生STOP阻止输入缓存被溢出。
SUSP
作业控制(job-control)挂起字符。如果支持作业控制,并且ISIG标记被设置的时候,那么这个字符回在输入的时候被识别出来。这个挂起字符会产生SIGTSTP信号,这个信号被发送给前台进程组中的所有进程。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
WERASE
word-erase字符。这个字符,在extended输入模式中,以及canonical输入模式中(IEXTEN和ICANON标记都被设置)被识别,并且导致前面的单词被删除。首先它向后跳过任何空白(空格或者tab键),然后向后跳过前面的token,导致光标停止在前面token所在的第一个字符。一般来说,token会在遇到一个空白的时候结束(其实就是一直向前删除,直至遇到空白)。我们可以修改这个特性。然而得通过设置ALTWERASE标记。这个标记导致当遇到第一个非字母和数字的字符的时候,之前的token结束。这个字符处理之后就会被丢弃(也就是说,它不会被传递给相应的进程)。
另外一个我们需要为终端设备定义的字符是BREAK字符。BREAK其实并不真正是一个字符,但是它表示一个在串口数据异步传输的时候发生的一个条件。一个BREAK条件会根据串行接口,以各种不同方式发送信号给设备驱动。
大多数原来的串行终端有一个按键上面标记着BREAK,这个按键可以产生BREAK条件,所以大多数人才以为BREAK是一个字符。有些新的终端键盘没有BREAK按键了,在PC机上面,break可能会被用作其他目的。例如,windows命令解释器可能会通过键入Control-BREAK的时候被中断。
对于异步串行数据传输,一个BREAK就是一系列的0比特,这些0持续的时间比发送一个字节的时间要长。整个0比特序列被当做一个单个的BREAK。在后面,我们将会看到,如何通过函数tcsendbreak函数发送一个BREAK。
参考: