终端 I/O 综述
程序员文章站
2022-07-01 09:14:43
...
终端设备可认为是由内核中的终端驱动程序控制的。每个终端设备都有一个输入队列和输出队列。如下图所示。
对该图要说明以下几点:
1)如果打开了回显功能,则在输入队列和输出队列之间有个隐含的连接。
2)当一个特定设备的输入队列中的字节数达到 MAX_INPUT(见unix限制一节)时,系统的行为将依赖于实现(大多数 UNIX 系统回显响铃字符)。
3)图中没有显示另一个输入限制 MAX_CANON,它是一个规范输入行的最大字节数。
4)虽然输出队列的长度通常也是有限的,但程序并不能获得该长度,因为内核会在输出队列将要填满时使写进程休眠,直到写队列中有可用的空间。
5)后面会介绍如何使用 tcflush 函数冲洗输入或输出队列,以及在介绍 tcsetattr 函数时,将了解到如何通知系统只有在输出队列为空时,才能改变一个终端的属性,也可以让它在改变终端属性时丢弃输入队列中的所有东西。
多数 UNIX 系统在一个称为终端行规程(terminal line discipline)的模块中进行全部的规范处理,可将其视作位于内核通用读、写函数和实际设备驱动程序之间的一个盒子,通过它我们能够设置终端的特殊字符(如退格、行删除、中断等)。如图所示。
termios 结构包含了所有可以检测和更改的终端设备特性。
其中,输入标志 c_iflag 通过终端设备驱动程序控制字符的输入(如剥除输入字节的第 8 位,允许输入奇偶校验),输出标志 c_oflag 则控制驱动程序输出(如执行输出处理、将换行符转换成 CR/LF),控制标志 c_cflag 影响 RS-232 串行线(如忽略调制解调器的状态线、每个字符的一个或两个停止位),本地标志 c_lflag 影响驱动程序和用户之间的接口(如回显打开或关闭、可视地擦除字符、允许终端产生的信号以及对后台输出的作业控制停止信号),c_cc 数组包含了所有可以更改的控制字符。另外,类型 tcflag_t 经常被定义为 unsigned int 或者 unsigned long,类型 cc_t 常被定义为 unsigned char。
下面的四个图列出了所有可以更改以影响终端设备特性的终端标志,其中的每个选项的详细说明见终端属性和选项标志一节。
要检测和更改终端设备的这些特性,可以使用下图列出的用来操作终端设备的各个函数。
其中 tcgetpgrp、tcgetsid 和 tcsetpgrp 函数在进程组、会话和控制终端一节中已经说明,其它函数会在后面介绍。注意,Single UNIX Specification 没有对终端设备使用经典的 ioctl 函数(见终端I/O函数 ioctl一节),而是使用这些函数的原因是:对于终端设备的 ioctl 函数,其最后一个参数的数据类型随执行动作的不同而改变。因此,不可能对参数进行类型检查。这些函数之间的关系如下图所示。
这些选项既可以在程序中使用 tcgetattr 和 tcsetattr 函数进行检查和更改,也可以在 shell 中用 stty(1) 命令进行检查和更改。stty(1)命令其实就相当于上表中的前 6 个函数的接口。使用“-a”选项,stty(1) 将显示终端的所有选项,如下图所示。
其中,名字前有一个连字符的选项表示被禁用。最后 4 行显示了各终端特殊字符(见终端特殊输入字符)的当前设置。
另外,stty 命令使用它的标准输入获得和设置终端的选项标志。这意味着如果希望了解名为 tty1a 的终端的设置,可以键入:
stty -a < /dev/tty1a
对该图要说明以下几点:
1)如果打开了回显功能,则在输入队列和输出队列之间有个隐含的连接。
2)当一个特定设备的输入队列中的字节数达到 MAX_INPUT(见unix限制一节)时,系统的行为将依赖于实现(大多数 UNIX 系统回显响铃字符)。
3)图中没有显示另一个输入限制 MAX_CANON,它是一个规范输入行的最大字节数。
4)虽然输出队列的长度通常也是有限的,但程序并不能获得该长度,因为内核会在输出队列将要填满时使写进程休眠,直到写队列中有可用的空间。
5)后面会介绍如何使用 tcflush 函数冲洗输入或输出队列,以及在介绍 tcsetattr 函数时,将了解到如何通知系统只有在输出队列为空时,才能改变一个终端的属性,也可以让它在改变终端属性时丢弃输入队列中的所有东西。
多数 UNIX 系统在一个称为终端行规程(terminal line discipline)的模块中进行全部的规范处理,可将其视作位于内核通用读、写函数和实际设备驱动程序之间的一个盒子,通过它我们能够设置终端的特殊字符(如退格、行删除、中断等)。如图所示。
termios 结构包含了所有可以检测和更改的终端设备特性。
#include <termios.h> struct termios{ tcflag_t c_iflag; // input flags tcflag_t c_oflag; // output flags tcflag_t c_cflag; // control flags tcflag_t c_lflag; // local flags cc_t c_cc[NCCS]; // control characters };
其中,输入标志 c_iflag 通过终端设备驱动程序控制字符的输入(如剥除输入字节的第 8 位,允许输入奇偶校验),输出标志 c_oflag 则控制驱动程序输出(如执行输出处理、将换行符转换成 CR/LF),控制标志 c_cflag 影响 RS-232 串行线(如忽略调制解调器的状态线、每个字符的一个或两个停止位),本地标志 c_lflag 影响驱动程序和用户之间的接口(如回显打开或关闭、可视地擦除字符、允许终端产生的信号以及对后台输出的作业控制停止信号),c_cc 数组包含了所有可以更改的控制字符。另外,类型 tcflag_t 经常被定义为 unsigned int 或者 unsigned long,类型 cc_t 常被定义为 unsigned char。
下面的四个图列出了所有可以更改以影响终端设备特性的终端标志,其中的每个选项的详细说明见终端属性和选项标志一节。
要检测和更改终端设备的这些特性,可以使用下图列出的用来操作终端设备的各个函数。
其中 tcgetpgrp、tcgetsid 和 tcsetpgrp 函数在进程组、会话和控制终端一节中已经说明,其它函数会在后面介绍。注意,Single UNIX Specification 没有对终端设备使用经典的 ioctl 函数(见终端I/O函数 ioctl一节),而是使用这些函数的原因是:对于终端设备的 ioctl 函数,其最后一个参数的数据类型随执行动作的不同而改变。因此,不可能对参数进行类型检查。这些函数之间的关系如下图所示。
这些选项既可以在程序中使用 tcgetattr 和 tcsetattr 函数进行检查和更改,也可以在 shell 中用 stty(1) 命令进行检查和更改。stty(1)命令其实就相当于上表中的前 6 个函数的接口。使用“-a”选项,stty(1) 将显示终端的所有选项,如下图所示。
其中,名字前有一个连字符的选项表示被禁用。最后 4 行显示了各终端特殊字符(见终端特殊输入字符)的当前设置。
另外,stty 命令使用它的标准输入获得和设置终端的选项标志。这意味着如果希望了解名为 tty1a 的终端的设置,可以键入:
stty -a < /dev/tty1a
上一篇: POSIX 信号量