终端I/O
索引:
- 终端I/O的工作方式
- termios结构
- 终端特殊输入字符
- POSIX终端I/O函数
- tcgetattr和tcsetattr
- 波特率函数
- 行控制函数
- 终端标识函数
- 规范方式
- 非规范方式
- break方式和raw方式
- 终端窗口
1.终端I/O的工作方式
终端I/O有两种不同的工作方式:
- 规范方式输入处理。在这种方式中,终端输入以行为单位进行处理。对于每个读要求,终端驱动程序最多返回一行。
- 非规范方式输入处理。输入字符不以行为单位进行装配。
如果不作特殊处理,则默认方式是规范方式。
V7和BSD类的终端驱动程序支持三种终端输入方式:
- 精细加工方式(输入装配成行,并对特殊字符进行处理);
- 原始方式(输入不装配成行,也不对特殊字符进行处理);
- cbreak方式(输入不装配成行,但对某些特殊字符进行处理)。
2.termios结构
struct termios { tcflag_t c_iflag; /* Input modes */ tcflag_t c_oflag; /* Output modes */ tcflag_t c_cflag; /* Control modes */ tcflag_t c_lflag; /* Local modes */ cc_t c_cc[NCCS]; /* Control characters */ };
各个字段的选项如下(不是所有UNIX系统都支持):
c_iflag:
- BRKINT:接到BREAK时产生SIGINT;
- ICRNL:将输入的CR转换为NL;
- IGNBRK:忽略BREAK条件;
- IGNCR:忽略CR;
- IGNPAR:忽略奇偶错字符;
- IMAXBEL:在输入队列空时振铃;
- INLCR:将输入的NL转换为CR;
- INPCK:打开输入奇偶校验;
- ISTRIP:剥除输入字符的第8位;
- IUCLC:将输入的大写字符转换成小写字符(仅SVR4);
- IXANY:使任一字符都重新起动输出;
- IXOFF:使起动/停止输入控制流起作用;
- IXON:使起动/停止输出控制流起作用;
- PARMRK:标记奇偶错;
c_oflag:
- BSDLY:退格延迟屏蔽(仅SVR4);
- CRDLY:CR延迟屏蔽(仅SVR4);
- FFDLY:换页延迟屏蔽(仅SVR4);
- NLDLY:NL延迟屏蔽(仅SVR4);
- OCRNL:将输出的CR转换为NL(仅SVR4);
- OFDEL:填充符为DEL,否则为NUL(仅SVR4);
- OFILL:对于延迟使用填充符(仅SVR4);
- OLCUC:将输出的小写字符转换为大写字符(仅SVR4);
- ONLCR:将NL转换为CR-NL;
- ONLRET:NL执行CR功能(仅SVR4);
- ONOCR:在0列不输出CR(仅SVR4);
- ONOEOT:在输出中删除EOT字符(仅4.3+BSD);
- OPOST:执行输出处理;
- OXTABS:将制表符扩充为空格(仅4.3+BSD);
- TABDLY:水平制表符延迟屏蔽(仅SVR4);
- VTDLY:垂直制表符延迟屏蔽(仅SVR4);
c_cflag:
- CCTS_OFLOW:输出的CTS流控制(仅4.3+BSD);
- CIGNORE:忽略控制标志(仅4.3+BSD);
- CLOCAL:忽略解制解调器状态行;
- CREAD:启用接收装置;
- CRTS_IFLOW:输入的RTS流控制(仅4.3+BSD);
- CSIZE:字符大小屏蔽;
- CSTOPB:送两个停止位,否则为1位;
- HUPCL:最后关闭时断开;
- MDMBUF:经载波的流控输出(仅4.3+BSD);
- PARENB:进行奇偶校;
- PARODD:奇校,否则为偶校;
c_lflag:
- ALTWERASE:使用替换WERASE算法(仅4.3+BSD);
- ECHO:进行回送;
- ECHOCTL:回送控制字符为^(char);
- ECHOE:可见擦除符;
- ECHOK:回送kill符;
- ECHOKE:kill的可见擦除;
- ECHONL:回送NL;
- ECHOPRT:硬拷贝的可见擦除方式;
- FLUSHO:刷清输出;
- ICANON:规范输入;
- IEXTEN:使扩充的输入字符处理起作用;
- ISIG:使终端产生的信号起作用;
- NOFLSH:在中断或退出键后不刷清;
- NOKERNINFO:STATUS不使内核输出(仅4.3+BSD);
- PENDIN:重新打印;
- TOSTOP:对于后台输出发送SIGTTOU;
- XCASE:规范大/小写表示(仅SVR4);
所有列出的选择标志(除屏蔽标志外)都用一或多位表示,而屏蔽标志则定义多位。屏蔽标志有一个定义名,每个值也有一个名字。例如,为了设置字符长度,首先用字符长度屏蔽标志CSIZE将表示字符长度的位清0,然后设置下列值之一:CS5、CS6、CS7或CS8。由SVR4支持的6个延迟值也有屏蔽标志:BSDLY、CRDLY、FFDLY、NLDLY、TABDLY和VTDLY。
各个标志的含义如下:
- ALTWERASE:(c_lflag, 4.3+BSD)此标志设置时,若输入了WERASE字符,则使用一个替换的字擦除算法。它不是向后移动到前一个白空字符为止,而是向后移动到第一个非字母、数字符为止。
- BRKINT:(c_iflag, POSIX.1)若此标志设置,而IGNBRK未设置,则在接到BREAK时,输入、输出队列被刷清,并产生一个SIGINT信号。如果此终端设备是一个控制终端,则将此信号送给前台进程组各进程。如果IGNBRK和BRKINT都没有设置,但是设置了PARMRK,则BREAK被读作为三个字节序列\377,\0和\0,如果PARMRK也没有设置,则BREAK被读作为单个字符\0。
- BSDLY:(c_oflag, SVR4)退格延迟屏蔽,此屏蔽的值是BS0或BS1。
- CCTS_OFLOW:(c_cflag, 4.3+BSD)输出的CTS流控制。
- CIGNORE:(c_cflag, 4.3+BSD)忽略控制标志。
- CLOCAL:(c_cflag, POSIX.1)如若设置,则忽略调制解调器状态线。这通常意味着该设备是本地连接的。若此标志未设置,则打开一个终端设备常常会阻塞到调制解调器回应。
- CRDLY:(c_oflag, SVR4)回车延迟屏蔽。此屏蔽的值是CR0、CR1、CR2和CR3。
- CREAD:(c_cflag, POSIX.1)如若设置,则接收装置被启用,可以接收字符。
- CRTS_IFLOW:(c_cflag, 4.3+BSD)输入的RTS流控制。
- CSIZE:(c_cflag, POSIX.1)此字段是一个屏蔽标志,它指明发送和接收的每个字节的位数。此长度不包括可能有的奇偶校验位。由此屏蔽定义的字段值是CS5、CS6、CS7和CS8,分别表示每个字节包含5、6、7和8位。
- CSTOPB:(c_cflag, POSIX.1)如若设置,则使用两位作为停止位,否则只使用一位作为停止位。
- ECHO:(c_lflag, POSIX.1)如若设置,则将输入字符回送到终端设备。在规范方式和非规范方式下都可以回送字符。
- ECHOCTL:(c_lflag, SVR4和4.3+BSD)如若设置并且ECHO也设置,则除ASCII TAB、ASCII NL、START和STOP字符外,其他ASCII控制符(ASCII字符集中的0~037)都被回送为^X,其中,X是相应控制字符代码值加0100所构成的字符。这就意味着ASCII Ctrl-A字符(01)被回送为^A。ASCII DELETE字符(0177)则回送为^?。如若此标志未设置,则ASCII控制字符按其原样回送。如同ECHO标志,在规范方式和非规范方式下此标志对控制字符回送都起作用。应当了解的是:某些系统回送EOF字符产生的作用有所不同,其原因是EOF的典型值是Ctrl-D,而这是ASCII EOT字符,它可能使某些终端挂断。
- ECHOE:(c_lflag, POSIX.1)如若设置并且ICANON也设置,则ERASE字符从显示中擦除当前行中的最后一个字符。这通常是在终端驱动程序中写三个字符序列:退格,空格,退格实现的。如若支持WERASE字符,则ECHOE用一个或若干个上述三字符序列擦除前一个字。如若支持ECHOPRT标志,则在这里所说明的ECHOE动作假定ECHOPRT标志没有设置。
- ECHOK:(c_lflag, POSIX.1)如若设置并且ICANON也设置,则KILL字符从显示中擦除当前行,或者输出NL字符(用以强调已擦除整个行)。如若支持ECHOKE标志,则这里的说明假定ECHOKE标志没有设置。
- ECHOKE:(c_lflag, SVR4和4.3+BSD)如若设置并且ICANON也设置,则回送KILL字符的方式是擦去行中的每一个字符。擦除每个字符的方法则由ECHOE和ECHOPRT标志选择。
- ECHONL:(c_lflag, POSIX.1)如若设置并且ICANON也设置,即使没有设置ECHO也回送NL字符。
- ECHOPRT:(c_lflag, SVR4和4.3+BSD)如若设置并且ICANON和ECHO也都设置,则ERASE字符(以及WERASE字符,若受到支持)使所有正被擦除的字符按它们被擦除的方式打印。在硬拷贝终端上这常常是有用的,这样可以确切地看到哪些字符正被擦去。
- FFDLY:(c_oflag, SVR4)换页延迟屏蔽。此屏蔽标志值是FF0或FF1。
- FLUSHO:(c_lflag, SVR4和4.3+BSD)如若设置,则刷清输出。当键入DISCARD字符时设置此标志,当键入另一个DISCARD字符时,此标志被清除。设置或清除此终端标志也可设置或清除此条件。
- HUPCL:(c_cflag, POSIX.1)如若设置,则当最后一个进程关闭此设备时,调制解调器控制线降至低电平(也就是调制解调器的连接断开)。
- ICANON:(c_lflag, POSIX.1)如若设置,则按规范方式工作。这使下列字符起作用:EOF、EOL、EOL2、ERASE、KILL、REPRINT、STATUS和WERASE。输入字符被装配成行。如果不以规范方式工作,则读请求直接从输入队列取字符。在至少接到MIN个字节或已超过TIME值之前,read将不返回。
- ICRNL:(c_iflag, POSIX.1)如若设置并且IGNCR未设置,即将接收到的CR字符转换成一个NL字符。
- IEXTEN:(c_lflag, POSIX.1)如若设置,则识别并处理扩充的、实现定义的特殊字符。
- IGNBRK:(c_iflag, POSIX.1)在设置时,忽略输入中的BREAK条件。关于BREAK条件是产生信号还是被读作为数据,请见BRKINT。
- IGNCR:(c_iflag, POSIX.1)如若设置,忽略接收到的CR字符。若此标志未设置,而设置了ICRNL标志则将接收到的CR字符转换成一个NL字符。
- IGNPAR:(c_iflag, POSIX.1)在设置时,忽略带有结构错误(非BREAK)或奇偶错的输入字节。
- IMAXBEL:(c_iflag, SVR4和4.3+BSD)当输入队列满时响铃。
- INLCR:(c_iflag, POSIX.1)如若设置,则接收到的NL字符转换成CR字符。
- INPCK:(c_iflag, POSIX.1)当设置时,使输入奇偶校验起作用。如若未设置INPCK,则使输入奇偶校验不起作用。奇偶“产生和检测”和“输入奇偶性检验”是不同的两件事。奇偶位的产生和检测是由PARENB标志控制的。设置该标志后使串行界面的设备驱动程序对输出字符产生奇偶位,对输入字符则验证其奇偶性。标志PARODD决定该奇偶性应当是奇还是偶。如果一个其奇偶性为错的字符已经来到,则检查INPCK标志的状态。若此标志已设置,则检查IGNPAR标志(以决定是否应忽略带奇偶错的输入字节),若不应忽略此输入字节,则检查PARMRK标志以决定向读进程应传送那种字符。
- ISIG:(c_lflag, POSIX.1)如若设置,则判别输入字符是否是要产生终端信号的特殊字符(INTR,QUIT,SUSP和DSUSP),若是,则产生相应信号。
- ISTRIP:(c_iflag, POSIX.1)当设置时,有效输入字节被剥离为7位。当此标志未设置时,则保留全部8位。
- IUCLC:(c_iflag, SVR4)将输入的大写字符映射为小写字符。
- IXANY:(c_iflag, SVR4和4.3+BSD)使任一字符都能重新起动输出。
- IXOFF:(c_iflag, POSIX.1)如若设置,则使起动-停止输入控制起作用。当终端驱动程序发现输入队列将要填满时,输出一个STOP字符。此字符应当由发送数据的设备识别,并使该设备暂停。此后,当已对输入队列中的字符进行了处理后,该终端驱动程序将输出一个START字符,使该设备恢复发送数据。
- IXON:(c_iflag, POSIX.1)如若设置,则使起动-停止输出控制起作用。当终端驱动程序接收到一个STOP字符时,输出暂停。在输出暂停时,下一个START字符恢复输出。如若未设置此标志,则START和STOP字符由进程读作为一般字符。
- MDMBUF:(c_cflag, 4.3+BSD)按照调制解调器的载波标志进行输出流控制。
- NLDLY:(c_oflag, SVR4)新行延迟屏蔽。此屏蔽的值是NL0和NL1。
- NOFLSH:(c_lflag, POSIX.1)按系统默认,当终端驱动程序产生SIGINT和SIGQUIT信号时,输入、出队列都被刷新。另外,当它产生SIGSUSP信号时,输入队列被刷新。如若设置了NOFLSH标志,则在这些信号产生时,不对输入、出队列进行刷新。
- NOKERNINFO:(c_lflag, 4.3+BSD)当设置时,此标志阻止STATUS字符使前台进程组的状态信息显示在终端上。但是不论本标志是否设置,STATUS字符使SIGINFO信号送至前台进程组中的所有进程。
- OCRNL:(c_oflag, SVR4)如若设置,将输出的CR字符映照为NL。
- OFDEL:(c_oflag, SVR4)如若设置,则输出填充字符是ASCII DEL,否则它是ASCII NUL,见OFILL标志。
- OFILL:(c_oflag, SVR4)如若设置,则为实现延迟,发送填充字符(ASCII DEL或ASCII NUL,见OFDEL标志),而不使用时间延迟。见6个延迟屏蔽:BSDLY,CRDLY,FFDLY,NLDLY,TABDLY以及VTDLY。
- OLCUC:(c_oflag, SVR4)如若设置,将小写字符映射为大写。
- ONLCR:(c_oflag, SVR4和4.3+BSD)如若设置,将输出的NL字符映照为CR-NL。
- ONLRET:(c_oflag, SVR4)如若设置,则输出的NL字符将执行回车功能。
- ONOCR:(c_oflag, SVR4)如若设置,则在0列不输出CR。
- ONOEOT:(c_oflag, 4.3+BSD)如若设置,则在输出中删除EOT字符(^D)。在将Ctrl-D解释为挂断的终端上这可能是需要的。
- OPOST:(c_oflag, POSIX.1)如若设置,则进行实现定义的输出处理。
- OXTABS:(c_oflag, 4.3+BSD)如若设置,制表符在输出中被扩展为空格。这与将水平制表延迟(TABDLY)设置为XTABS或TAB3产生同样效果。
- PARENB:(c_cflag, POSIX.1)如若设置,则对输出字符产生奇偶位,对输入字符则执行奇偶性检验。若PARODD已设置,则奇偶校验是奇校验,否则是偶校验。也见INPCK、IGNPAR和PARMRK标志部分。
- PARMRK:(c_iflag, POSIX.1),当设置时,并且IGNPAR未设置,则结构性错(非BREAK)和奇偶错的字节由进程读作为三个字符序列\377, \0和X,其中X是接收到的具有错误的字节。如若ISTRIP未设置,则一个有效的\377被传送给进程时为\377,\377。如若IGNPAR和PARMRK都未设置,则结构性错和奇偶错的字节都被读作为一个字符\0。
- PARODD:(c_cflag, POSIX.1)如若设置,则输出和输入字符的奇偶性都是奇,否则为偶。注意,PARENB标志控制奇偶性的产生和检测。
- PENDIN:(c_lflag, SVR4和4.3+BSD)如若设置,则在下一个字符输入时,尚未读的任何输入都由系统重新打印。这一动作与键入REPRINT字符时的作用相类似。
- TABDLY:(c_oflag, SVR4)水平制表延迟屏蔽。此屏蔽的值是TAB0、TAB1、TAB2或TAB3。XTABS的值等于TAB3。此值使系统将制表符扩展成空格。系统假定制表符所扩展的空格数到屏幕上最近一个8的倍数处为止。不能更改此假定。
- TOSTOP:(c_lflag, POSIX.1)如若设置,并且该实现支持作业控制,则将信号SIGTTOU送到试图与控制终端的一个后台进程的进程组。按默认,此信号暂停该进程组中所有进程。如果写控制终端的进程忽略或阻塞此信号,则终端驱动程序不产生此信号。
- VTDLY:(c_oflag, SVR4)垂直制表延迟屏蔽。此屏蔽的值是VT0或VT1。
- XCASE:(c_lflag, SVR4)如若设置,并且ICANON也设置,则认为终端是大写终端,所以输入都变换为小写。为了输入一个大写字符,在其前加一个\。与之类似,输出一个大写字符也在其前加一个\(这一标志已经过时,现在几乎所有终端都支持大、小写字符)。
3.终端特殊输入字符
POSIX.1定义了11个在输入时作特殊处理的字符。SVR4另外加了6个特殊字符,4.3+BSD则加了7个。见下表:
字符 | 说明 | c_cc下标 | 起作用,由: | 典型值 | POSIX.1 | SVR4 | 4.3+BSD | |
---|---|---|---|---|---|---|---|---|
字段 | 标志 | 扩充 | ||||||
CR | 回车 | 不能更改 | c_lflag | ICANON | \r | YES | ||
DISCARD | 擦除输出 | VDISCARD | c_lflag | IEXTEN | ^O | YES | YES | |
DSUSP | 延迟挂起(SIGTSTP) | VDUSP | c_lflag | ISIG | ^Y | YES | YES | |
EOF | 文件结束 | VEOF | c_lflag | ICANON | ^D | YES | ||
EOL | 行结束 | VEOL | c_lflag | ICANON | YES | |||
EOL2 | 替换的行结束 | VEOL2 | c_lflag | ICANON | YES | YES | ||
ERASE | 擦除字符 | VERASE | c_lflag | ICANON | ^H | YES | ||
INTR | 中断信号(SIGINT) | VINTR | c_lflag | ISIG | ^?, ^C | YES | ||
KILL | 擦行 | VKILL | c_lflag | ICANON | ^U | YES | ||
LNEXT | 下一个字列字符 | VLNEXT | c_lflag | IEXTEN | ^V | YES | YES | |
NL | 新行 | 不能更改 | c_lflag | ICANON | \n | YES | ||
QUIT | 退出信号(SIGQUIT) | VQUIT | c_lflag | ISIG | ^\ | YES | ||
REPRINT | 再打印全部输入 | VREPRINT | c_lflag | ICANON | ^R | YES | YES | |
START | 恢复输出 | VSTART | c_lflag | IXON/IXOFF | ^Q | YES | ||
STATUS | 状态要求 | VSTATUS | c_lflag | ICANON | ^T | YES | ||
STOP | 停止输出 | VSTOP | c_lflag | IXON/IXOFF | ^S | YES | ||
SUSP | 挂起信号(SIGTSTP) | VSUSP | c_lflag | ISIG | ^Z | YES | ||
WERASE | 擦除字 | VWERASE | c_lflag | ICANON | ^W | YES | YES |
我们称这些字符为特殊输入字符,但是其中有两个字符,STOP和START(Ctrl-S和Ctrl-Q)在输出时也对它们进行特殊处理。这些字符中的大多数在被终端驱动程序识别并进行特殊处理后都被丢弃,并不将它们传送给执行读终端操作的进程。例外的字符是新行符(NL,EOL,EOL2)和回车符(CR)。详细说明如下:
- CR:POSIX.1的回车符,不能更改此字符。以规范方式进行输入时识别此字符。当设置了ICANON(规范方式)和ICRNL(将CR映照为NL)以及没有设置IGNCR(忽略CR)时,将CR转换成NL,并产生与NL符相同的作用。此字符返回给读进程(多半是在转换成NL后)。
- DISCARD:SVR4和4.3+BSD的删除符。在扩充方式下(IEXTEN),在输入中识别此字符。在输入另一个删除符之前或删除条件被清除之前(见FLUSHO选择项)此字符使后续输出都被删除。在处理后此字符即被删除,不送向读进程。
- DSUSP:SVR4和4.3+BSD的延迟-挂起作业控制字符。在扩充方式下,若作业控制被挂起并且ISIG标志被设置,则在输入中识别此字符。与SUSP字符的相同处是:延迟-挂起字符产生SIGTSTP信号,它被送至前台进程组中的所有进程。但是并不是键入此字符时,而是在一个进程读控制终端时,此延迟-挂起字符才送向进程组。在处理后,此字符即被删除,不送向读进程。
- EOF:POSIX.1的文件结束符。以规范方式进行输入时识别此字符。当键入此字符时,等待被读的所有字节都立即传送给读进程。如果没有字节等待读,则返回0。在行首输入一个EOF符是向程序指示文件结束的正常方式。在处理后,此字符即被删除,不送向读进程。
- EOL:POSIX.1附加的行定界符,与NL作用相同。以规范方式进行输入时识别此字符。通常不使用此字符。此字符返回给读进程。
- EOL2:SVR4和4.3+BSD的附加行定界符,与NL作用相同。以规范方式输入时识别此字符。通常不使用此字符,此字符返回给读进程。
- ERASE:POSIX.1的擦除字符(退格)。以规范方式输入时识别此字符。它擦除行中的前一个字符,但不会超越行首字符擦除上一行中的字符。在处理后此字符即被擦除,不送向读进程。
- INTR:POSIX.1的中断字符。若设置了ISIG标志,则在输入中识别此字符。它产生SIGINT信号,该信号被送至前台进程组中的所有进程。在处理后,此字符即被删除,不送向读进程。
- KILL:POSIX.1的kill(杀死)字符。以规范方式输入时识别此字符。它擦除整个1行。在处理后,此字符即被删除,不送向读进程。
- LNEXT:SVR4和4.3+BSD的“字面上-下一个”字符。以规范方式输入时识别此字符,它使下一个字符的任何特殊含意都被忽略。这对本节提及的所有特殊字符都起作用。使用这一字符可向程序键入任何字符。在处理后,LNEXT字符即被删除,但输入的下一个字符则被传送给读进程。
- NL:POSIX.1的新行字符,它也被称为行定界符。不能更改此字符。以规范方式输入时识别此字符。此字符返回给读进程。
- QUIT:POSIX.1的退出字符。若设置了ISIG标志,则在输入中识别此字符。它产生SIGQUIT信号,该信号又被送至前台进程组中的所有进程。在处理后,此字符即被删除,不送向读进程。
- REPRINT:SVR4和4.3+BSD的再打印字符。以扩充规范方式(设置了IEXTEN和ICANON标志)进行输入时识别此字符。它使所有未读的输入被输出(再回送)。在处理后,此字符即被删除,不送向读进程。
- START:POSIX.1的起动字符。若设置了IXON标志则在输入中识别此字符;若设置IXOFF标志,则作为输出自动产生此字符。在IXON已设置时接收到的START字符使停止的输出(由以前输入的STOP字符造成)重新起动。在此情形下,在处理后,此字符即被删除,不送向读进程。在IXOFF标志设置时,若输入不会使输入缓存溢出,则终端驱动程序自动地产生一START字符以恢复以前被停止的输入。
- STATUS:4.3+BSD的状态-要求字符。以扩充、规范方式进行输入时识别此字符。它产生SIGINFO信号,该信号又被送至前台进程组中的所有进程。另外,如果没有设置NOKERNINFO标志,则有关前台进程组的状态信息也显示在终端上。在处理后,此字符即被删除,不送向读进程。
- STOP:POSIX.1的停止字符。若设置了IXON标志,则在输入中识别此字符;若IXOFF标志已设置则作为输出自动产生此字符。在IXON已设置时接收到STOP字符则停止输出。在此情形下,在处理后删除此字符,不送向读进程。当输入一个START字符后,停止的输出重新起动。在IXOFF设置时,终端驱动程序自动地产生一个STOP字符以防止输入缓存溢出。
- SUSP:POSIX.1的挂起作业控制字符。若支持作业控制并且ISIG标志已设置,则在输入中识别此字符。它产生SIGTSTP信号,该信号又被送至前台进程组的所有进程。在处理后,此字符即被删除,不送向读进程。
- WERASE:SVR4和4.3+BSD的字擦除字符。以扩充、规范方式进行输入时识别此字符。它使前一个字被擦除。首先,它向后跳过任一白空字符(空格或制表符),然后向后越过前一记号,使光标处在前一个记号的第一个字符位置上。通常,前一个记号在碰到一个白空字符时即终止。但是,可用设置ALTWERASE标志来改变这一点。此标志使前一个记号在碰到第一个非字母、数字符时即终止。在处理后,此字符即被删除,不送向读进程。
需要为终端设备定义的另一个“字符”是BREAK。BREAK实际上并不是一个字符,而是在异步串行数据传送时发生的一个条件。
在POSIX.1的11个特殊字符中,可将其中9个更改为几乎任何值。不能更改的两个特殊字符是新行符和回车符(\n和\r),有些实现也不允许更改STOP和START字符。为了进行修改,只要更改termios结构中c_cc数组的相应项,该数组中的元素都用名字作为下标进行引用,每个名字都以字母V开头。
POSIX.1可选地允许禁止使用这些字符。若_POSIX_VDISABLE有效,则_POSIX_VDISABLE的值可存放在c_cc数组的相应项中以禁止使用该特殊字符。可以用pathconf和fpathconf函数查询此特征。
4.POSIX终端I/O函数
tcgetattr | 取属性(termios结构); |
tcsetattr | 设置属性(termios结构); |
cfgetispeed | 得到输入速度; |
cfgetospeed | 得到输出速度; |
cfsetispeed | 设置输入速度; |
cfsetospeed | 设置输出速度; |
tcdrain | 等待所有输出都被传输; |
tcflow | 挂起传输或接收; |
tcflush | 刷清未决输入和/或输出; |
tcsendbreak | 送BREAK字符; |
tcgetpgrp | 得到前台进程组ID; |
tcsetpgrp | 设置前台进程组ID; |
5.tcgetattr和tcsetattr
#include <termios.h> int tcgetattr(int filedes, struct termios *termptr); int tcsetattr(int filedes, int opt, const struct termios *termptr); 两个函数返回:若成功则为0,若出错则为-1
这两个函数都有一个指向termios结构的指针作为其参数,它们返回当前终端的属性,或者设置该终端的属性。因为这两个函数只对终端设备进行操作,所以若filedes并不引用一个终端设备则出错返回,errno设置为ENOTTY。
tcsetattr的参数opt使我们可以指定在什么时候新的终端属性才起作用。opt可以指定为下列常数中的一个:
- TCSANOW:更改立即发生;
- TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选择项。
- TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被删除(刷清)。
tcsetattr函数的返回值易于产生混淆。如果它执行了任意一种所要求的动作,即使未能执行所有要求的动作,它也返回0(表示成功)。如果该函数返回0,则我们有责任检查该函数是否执行了所有要求的动作。这就意味着,在调用tcsetattr设置所希望的属性后,需调用tcgetattr,然后将实际终端属性与所希望的属性相比较,以检测两者是否有区别。
6.波特率函数
波特率(baud rate)是一个历史沿用的术语,现在它指的是“位/每秒”。虽然大多数终端设备对输入和输出使用同一波特率,但是只要硬件许可,可以将它们设置为两个不同值。
#include <termios.h> speed_t cfgetispeed(const struct termios *termptr); speed_t cfgetospeed(const struct termios *termptr); 两个函数返回:波特率值 int cfsetispeed(struct termios *termptr, speed_t speed); int cfsetospeed(struct termios *termptr, speed_t speed); 两个函数返回:若成功为0,出错为-1
两个cfget函数的返回值,以及两个cfset函数的speed参数都是下列常数之一:B50、B75、B110、B134、B150、B200、B300、B600、B1200、B1800、B2400、B4800、B9600、B19200或B38400。常数B0表示“挂断”。在调用tcsetattr时将输出波特率指定为B0,则调制解调器的控制线就不再起作用。
使用这些函数时,应当理解输入、输出波特率是存放在termios结构中的。在调用任一cfget函数之前,先要用tcgetattr获得设备的termios结构。与此类似,在调用任一cfset函数后,应将波特率设置到termios结构中。为使这种更改影响到设备,应当调用tcsetattr函数。如果所设置的波特率有错,则在调用tcsetattr之前,不会发现这种错误。
7.行控制函数
#include <termios.h> int tcdrain(int filedes); int tcflow(int filedes, int action); int tcflush(int filedes, int queue); int tcsendbreak(int filedes, int duration); 四个函数返回:若成功则为0,若出错则为-1
其中,参数filedes引用一个终端设备,否则出错返回,errno设置为ENOTTY。
tcdrain函数等待所有输出都被发送。
tcflow用于对输入和输出流控制进行控制。action参数应当是下列四个值之一:
- TCOOFF:输出被挂起;
- TCOON:以前被挂起的输出被重新起动;
- TCIOFF:系统发送一个STOP字符。这将使终端设备暂停发送数据;
- TCION:系统发送一个START字符。这将使终端恢复发送数据。
tcflush函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。queue参数应当是下列三个常数之一:
- TCIFLUSH:刷清输入队列;
- TCOFLUSH:刷清输出队列;
- TCIOFLUSH:刷清输入、输出队列;
tcsendbreak函数在一个指定的时间区间内发送连续的0位流。若duration参数为0,则此种发送延续0.25~ 0.5秒之间。POSIX.1说明若duration非0,则发送时间依赖于实现。
8.终端标识函数
POSIX.1提供了一个运行时函数,可被调用来决定控制终端的名字:
#include <stdio.h> char * ctermid(char *ptr);
如果ptr是非空,则它被认为是一个指针,指向长度至少为L_ctermid字节的数组,进程的控制终端名存放在该数组中。常数L_ctermid定义在<stdio.h>中。若ptr是一个空指针,则该函数为数组(通常作为静态变量)分配空间。同样,进程的控制终端名存放在该数组中。
在这两种情况中,该数组的起始地址被作为函数值返回。因为大多数UNIX系统都使用/dev/tty作为控制终端名,所以此函数的主要作用是帮助提高向其他操作系统的可移植性。
其他终端标识函数还有:
#include <unistd.h> int isatty(int filedes); 返回:若为终端设备则为1(真),否则为0(假) char *ttyname(int filedes); 返回:指向终端路径名的指针,若出错则为NULL
如果文件描述符引用一个终端设备,则isatty返回真,而ttyname则返回在该文件描述符上打开的终端设备的路径名。
9.规范方式
规范方式发一个读请求,当一行已经输入后,终端驱动程序即返回。许多条件造成读返回:
- 所要求的字节数已读到时读即返回。无需读一个完整的行。如果读了部分行,那么也不会丢失任何信息―下一次读从前一次读的停止处开始。
- 当读到一个行定界符时,读返回。在规范方式中,下列字符被解释为“行结束”:NL、EOL、EOL2和EOF。另外,如若已设置ICRNL,但未设置IGNCR,则CR字符的作用与NL字符一样,所以它也终止一行。在这五个行定界符中,其中只有一个EOF符在终端驱动程序对其进行处理后即被删除。其他四个字符则作为该行的最后一个字符返回调用者。
- 如果捕捉到信号而且该函数并不自动再起动,则读也返回。
10.非规范方式
将termios结构中c_lflag字段的ICANON标志关闭就使终端处于非规范方式。在非规范方式中,输入数据不装配成行,不处理下列特殊字符:ERASE、KILL、EOF、NL、EOL、EOL2、CR、REPRINT、STATUS和WERASE。
在非规范方式下,由于不是每次返回一行,解决读的方法是:当已读了指定量的数据后,或者已经过了给定量的时间后,即通知系统返回。
这种技术使用termios结构中c_cc数组的两个变量:MIN和TIME。c_cc数组中的这两个元素的下标名为:VMIN和VTIME。
MIN说明一个read返回前的最小字节数。TIME说明等待数据到达的分秒数(秒的1/10为分秒)。有下列四种情形:
- 情形A:MIN > 0, TIME > 0。TIME说明一个字节间的计时器,在接到第一个字节时才起动它。在该计时器超时之前,若已接到MIN个字节,则read返回MIN个字节。如果在接到MIN个字节之前,该计时器已超时,则read返回已接收到的字节(因为只有在接到第一个字节时才起动,所以在计时器超时时,至少返回1个字节)。在这种情形中,在接到第一个字节之前,调用者阻塞。如果在调用read时数据已经可用,则这如同在read后,数据立即被接收到一样。
- 情形B:MIN > 0 , TIME = = 0。已经接到了MIN个字节时,read才返回。这可以造成read无限期的阻塞。
- 情形C:MIN = = 0,TIME > 0。TIME指定了一个调用read时起动的读计时器。(与情形A相比较,两者是不同的)。在接到1个字节或者该计时器超时时,read即返回。如果是计时器超时,则read返回0。
- 情形D :MIN = = 0,TIME = = 0。如果有数据可用,则read最多返回所要求的字节数。如果无数据可用,则read立即返回0。
在所有这些情形中,MIN只是最小值。如果程序要求的数据多于MIN个字节,那么它可能能接收到所要求的字节数。这也适用于MIN = = 0的情形A和B。
11.break方式和raw方式
对cbreak方式的定义是:
- 非规范方式。这种方式不对某些输入特殊字符进行处理。这种方式仍对信号进行处理,所以用户可以键入任一终端产生的信号。调用者应当捕捉这些信号,否则这种信号就可能终止程序,并且终端将仍处于cbreak方式。作为一般规则,在编写更改终端方式的程序时,应当捕捉大多数信号,以便在程序终止前恢复终端方式;
- 关闭回送(ECHO)标志;
- 每次输入一个字节。为此将MIN设置为1,将TIME设置为0。至少有一个字节可用时,read再返回。
对原始方式的定义是:
- 非规范方式。另外,还关闭了对信号产生字符(ISIG)和扩充输入字符的处理(IEXTEN)。关闭BRKINT,这样就使BREAK字符不再产生信号;
- 关闭回送(ECHO)标志;
- 关闭ICRNL、INPCK、ISTRIP和IXON标志。于是:不再将输入的CR字符变换为NL(ICRNL)、使输入奇偶校验不起作用(INPCK)、不再剥离输入字节的第8位(ISTRIP)、不进行输出流控制(IXON);
- 8位字符(CS8),不产生奇偶位,不进行奇偶性检测(PARENB);
- 禁止所有输出处理(OPOST);
- 每次输入一个字节(MIN = 1,TIME = 0)。
12.终端窗口
内核为每个终端和伪终端保存一个winsize结构:
struct winsize { unsigned short ws_row; /* rows, in characters */ unsigned short ws_col; /* columns, in character */ unsigned short ws_xpixel; /* horizontal size, pixels */ unsigned short ws_ypixel; /* vertical size, pixels */ };
此结构的作用是:
- 用ioctl的TIOCGWINSZ命令可以取此结构的当前值。
- 用ioctl的TIOCSWINSZ命令可以将此结构的新值存放到内核中。如果此新值与存放在内核中的当前值不同,则向前台进程组发送SIGWINCH信号。此信号的系统默认动作是忽略。
- 除了存放此结构的当前值以及在此值改变时产生一个信号以外,内核对该结构不进行任何其他操作。对结构中的值进行解释完全是应用程序的工作。
提供这种功能的目的是,当窗口大小发生变化时通知应用程序(例如vi编辑程序)。应用程序接到此信号后,它可以取得窗口大小的新值,然后重绘屏幕。