1、综述
终端I/O有两种不同的工作模式:
(1)规范模式输入处理。在这种模式中,对终端输入以行为单位进行处理。对于每个读请求,终端驱动程序最多返回一行。
(2)非规范模式输入处理。输入字符不装配成行。
可以认为终端设备是由通常位于内核中的终端驱动程序控制的。每个终端设备都有一个输入队列和一个输出队列:
大多数UNIX系统在一个称为终端行规程的模块中进行全部的规范处理。这个模块位于内核通用读、写函数和实际设备驱动程序之间。
所有可以检测和更改的终端设备特性都包含在termios结构中。该结构定义在头文件<termios.h>
中。
struct termios {
tcflag_t c_iflag;
tcflag_t c_oflag;
tcflag_t c_cflag;
tcflag_t c_lflag;
cc_t c_cc[NCCS]; /*control characters*/
};
输入标志通过终端设备驱动程序控制字符的输入,输出标志则控制驱动程序输出,控制标志影响RS-232串行线,本地标志影响驱动程序和用户之间的接口。
2、特殊输入字符
注:换行符和回车符(\n和\r)不能更改,其他特殊字符可以任意更改。
为了更改,只需要修改termios结构中c_cc数组的相应项。该数组中的元素都用名字作为下标进行引用,每个名字都以字母V开头。
若将c_cc数组中的某项设置为_POSIX_VDISABLE的值,则禁止使用相应特殊字符。
下面较2详细地说明各个特殊字符(特殊输入字符):
3、获得和设置终端属性
为了获得和设置termios结构,可以调用tcgetattr和tcsetattr函数。
#include <termios.h>
int tcgetattr(int fd,struct termios *termptr);
int tcsetattr(int fd,int opt,const struct termios *termptr);
//若成功,返回0;若出错,返回-1
注:
(1)因为这两个函数只对终端设备进行操作,所以若fd没有引用终端设备则出错返回-1,errno设置为ENOTTY。
(2)tcsetattr函数如果执行了任意一种所要求的动作,即使未能执行所有要求的动作,它也返回OK。因此在调用tcsetattr设置所希望的属性后,需调用tcgetattr,然后将实际终端属性与所希望的属性相比较,以检测两者是否有区别。
参数opt使我们可以指定在什么时候新的终端属性才起作用:
TCSANOW:更改立即发生。
TCSADRAIN:发送了所有输出后更改才发生。若更改输出参数则应使用此选项。
TCSAFLUSH:发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被丢弃(冲洗)。
4、终端选项标志
5、波特率函数
大多数终端设备对输入和输出使用同一波特率,但只要硬件许可,可以将它们设置为两个不同值。
#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);
//若成功,返回-;若出错,返回-1
speed参数:B50、B75、B110、B134、B200、B300、B600、B1200、B1800、B2400、B4800、B9600、B19200、B38400
在调用两个cfget函数中的任意一个之前,要先用tcgetattr获得设备的termios结构。与此类似,在调用两个cfset函数中的任意一个之后,要做的就是在termios结构中设置波特率。为使这种更改影响到设备,应当调用tcsetattr函数。
6、行控制函数
#include <termios.h>
int tcdrain(int fd);
int tcflow(int fd,int action);
int tcflush(int fd,int queue);
int tcsendbreak(int fd,int duration);
//若成功,返回-;若出错,返回-1
tcdrain函数等待所有输出都被传递。
tcflow函数用于对输入和输出流控制进行控制。action参数必定是下列4个值之一:
TCOOFF:输出被挂起。
TCOON:重新启动以前被挂起的输出。
TCIOFF:系统发送一个STOP字符,这将使终端设备停止发送数据。
TCION:系统发送一个STATRT字符,这将使终端设备恢复发送数据。
tcflush函数冲洗(抛弃)输入缓冲区(其中的数据是终端驱动程序已接收到,但用户尚未读取的)或输出缓冲区(其中的数据是用户程序已经写入,当尚未被传递的)。queue参数必定是下列3个常量之一:
TCIFLUSH:冲洗输入队列。
TCOFLUSH:冲洗输出队列。
TCIOFLUSH:冲洗输入队列和输出队列。
tcsendbreak函数在一个指定的时间区内发送连续的0值位流。
7、终端标志
确定控制终端的名字:
#include <stdio.h>
char *ctermid(char *ptr);
//若成功,返回指向控制终端名的指针;若出错,返回指向空字符串的指针
如果文件描述符引用一个终端设备,则isatty返回真。ttyname返回的是在该文件描述符上打开的终端设备的路径名:
#inlcude <unistd.h>
int isatty(int fd);
//若为终端设备,返回1;否则,返回0
char *ttyname(int fd);
指向终端路径名的指针;若出错,返回NULL
8、规范模式
规范模式很简单:发一个读请求,当一行已经输入后,终端驱动程序即返回。以下几个条件造成读返回:
- 所请求的字节数已读到时,读返回。
- 当读到一个行定界符时,读返回。(NL、EOL、EOL2、EOF、CR)
- 如果捕捉到信号,并且该函数不再自动重启,则读也返回。
9、非规范模式
可以通过关闭termios结构中c_flags字段的ICANON标志来指定非规范模式。在非规范模式中,输入数据不装配成行,不处理下列特殊字符:ERASE、KILL、EOF、NL、EOL、EOL2、CR、REPRINT、STATUS和WERASE。
当已读了指定量的数据后,或者已经超过了给定量的时间后,即通知系统返回。这种技术使用了termios结构中c_cc数组的两个变量:MIN和TIME。c_cc数组中的这两个元素的下标名为VMIN和VTIME。
10、终端窗口大小
struct winsize{
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel;
unsigned short ws_ypixel;
};
- 用ioctl的TIOCGWINSZ命令可以取此结构的当前值。(ioctl(fd,TIOCGWINSZ,(char *)&winsize)
- 用ioctl的TIOCSWINSZ命令可以将此结构的新值存储到内核中。如果此新值与存储在内核中的当前值不同,则前台进程组会收到SIGWINCH信号(默认动作忽略)。