1.综述
终端I/O有两种不同的工作方式:
(1)规范方式输入处理。在这种方式中,终端输入以行为单位进行处理。对于每个读要求,终端驱动程序最多返回一行。
(2)非规范方式输入处理。输入字符不以行为单位进行装配。
终端设备的输入、输出队列的逻辑结构:(图片来自https://www.cnblogs.com/nufangrensheng/p/3575752.html)
所有我们可以检测和更改的终端设备特性都包含在termios结构中。
struct termios{
tcflag_t c_iflag; //input flags
tcflag_t c_oflags; //output flags
tcflag_t c_cflags; //control flags
tcflag_t c_lflags; //local flags
cc_t c_cc[NCCS]; //control characters
};
终端标志:
POSIX.1终端I / O函数:
tcgetattr 取属性(termios结构)
tcsetattr 设置属性(termios结构)
cfgetispeed 得到输入速度
cfgetospeed 得到输出速度
cfsetispeed 设置输入速度
cfsetospeed 设置输出速度
tcdrain 等待所有输出都被传输
tcflow 挂起传输或接收
tcflush 刷清未决输入和/或输出
tcsendbreak 送BREAK字符
tcgetpgrp 得到前台进程组ID
tcsetpgrp 设置前台进程组ID
上述函数的关系:
2.特殊输入字符
3.获得和设置终端属性
使用函数tcgetattr和tcsetattr可以获得或设置termios。这样也就可以检测和修改各种终端选择标志和特殊字符,以使终端按我们所希望的方式进行操作。
4.波特率函数
功能:获取和设置终端波特率。
#include <termios.h>
/* 返回值:若成功则返回波特率值 */
speed_t cfgetispeed(const struct termios *termptr);/* 获取输入波特率 */
speed_t cfgetospeed(const struct termios *termptr);/* 获取输出波特率 */
/* 返回值:若成功则返回0,出错则返回-1;*/
int cfsetispeed(struct termios *termptr, speed_t speed);/* 设置输入波特率 */
int cfsetospeed(struct termios *termptr, speed_t speed);/* 设置输出波特率 */
5.行控制函数
下列四个函数提供了终端设备的行控制能力。其中,参数filedes引用一个终端设备,否则出错返回,errno设置为ENOTTY。
#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
tcdrain函数等待所有输出都被发送。tcflow用于对输入和输出流控制进行控制。action参数应当是下列四个值之一。
(1)TCOOFF 输出被挂起。
(2)TCOON以前被挂起的输出被重新起动。
(3)TCIOFF系统发送一个STOP字符。这将使终端设备暂停发送数据。
(4)TCION系统发送一个START字符。这将使终端恢复发送数据。
tcflush函数刷清(抛弃)输入缓存(终端驱动程序已接收到,但用户程序尚未读)或输出缓存(用户程序已经写,但尚未发送)。queue参数应当是下列三个常数之一:
(1)TCIFLUSH刷清输入队列。
(2)TCOFLUSH 刷清输出队列。
(3)TCIOFLUSH刷清输入、输出队列。
6.终端标识
ctermid函数:可被调用来决定控制终端的名字。
另外两个与终端标识有关的函数是isatty和ttyname。如果文件描述符引用一个终端设备,则isatty返回真,而ttyname则返回在该文件描述符上打开的终端设备的路径名。
7.规范方式
规范方式很简单—发一个读请求,当一行已经输入后,终端驱动程序即返回。许多条件造成读返回。
(1)所要求的字节数已读到时读即返回。无需读一个完整的行。如果读了部分行,那么也不会丢失任何信息—下一次读从前一次读的停止处开始。
(2)当读到一个行定界符时,读返回。回忆11 . 3节,在规范方式中,下列字符被解释为“行结束”:NL、EOL、EOL2和EOF。另外,如若已设置ICRNL,但未设置IGNCR,则CR字符的作用与NL字符一样,所以它也终止一行。在这五个行定界符中,其中只有一个EOF符在终端驱动程序对其进行处理后即被删除。其他四个字符则作为该行的最后一个字符返回调用者。
(3)如果捕捉到信号而且该函数并不自动再起动,则读也返回。
getpass函数:读入用户在终端上键入的口令。为了读口令,该函数必须禁止回送,但仍可使终端以规范方式进行工作,因为用户在键入口令后,一定要键入回车,这样也就构成了一个完整行。
8.非规范方式
将termios结构中clflag字段的ICANON标志关闭就使终端处于非规范方式。在非规范方式中,输入数据不装配成行,不处理下列特殊字符:ERASE、KILL、EOF、NL、EOL、EOL2、CR、REPRINT、STATUS和WERASE。
&emap; 如前所述,规范方式很容易—系统每次返回一行。但在非规范方式下,系统怎样才能知道在什么时候将数据返回给我们呢?如果它一次返回一个字节,那么系统开销就很大。(每次读一个字节的开销很大。每次使返回的数据加倍,就使系统调用的开销减半。)在起动读数据之前,往往不知道要读多少数据,所以系统不能总是返回多个字节。解决方法是:当已读了指定量的数据后,或者已经过了给定量的时间后,即通知系统返回。
9.termcap,terminfo和curses
termcap的意思是终端性能(terminal capability),它涉及到文本文件/etc/termcap和一套读此文件的例程。termcap这种技术是在伯克利为了支持vi编辑器而发展起来的。termcap文件包含了对各种终端的说明:终端支持哪些功能(行、列数、是否支持退格等),如何使终端执行某些操作(清屏、将光标移动到指定位置等)。把这些信息从需要编译的程序中取出来并把它们放在易于编辑的文本文件中,这样就使得vi能在很多不同的终端上运行。
然后,支持termcap文件的一套例程也从vi编辑程序中抽取出来,放在一个单独的curses(光标)库中。为使这套库可被要进行屏幕处理的任何程序使用,增加了很多功能。
termcap这种技术不是很完善的。当越来越多的终端被加到该数据文件中时,为了找到一个特定的终端就需使用较长的时间扫描此文件。此数据文件也只用两个字符的名字来标识不同的终端属性。这些缺陷导致开发另一种新技术—terminfo及与其相关的curses库。在terminfo中,终端说明基本上是文本说明的编译版本,在运行时易于快速定位。terminfo由SVR2开始使用,此后所有系统V版本都使用它。