Linux user 没有不和终端打交道的,所以做 Linux 下的开发,必然会涉及到终端的操作。
终端分为标准模式和非标准模式。
默认状态下,只有在用户按下回车键后,程序才能读到终端的输入,这种处理方式被成为标准模式(standard mode),所有的输入都是基于行处理,在一个输入行完成之前(通常是用户按下回车键之前),应用程序读不到用户输入的任何字符。
非标准模式则可以与标准模式相对应,在用户按下回车键前,读取数据到程序。
以下是一些与终端操作相关的函数
isatty 函数
isatty 原型 1 2 3 | |
isatty
作用:检查标准输出是否被重定向,实质是检查底层文件描述符是否关联到了一个终端而已。
如果打开的文件描述符fd
连接到了一个终端,则系统调用isatty
返回1
,否则返回0
。
isatty
只能对文件描述符进行操作,所以需要与fileno
函数结合使用。
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
1 2 3 4 | |
termios 结构
termios
数据结构和相关函数调用都定义在头文件termios.h
和curses.h
。 最小化的termios
结构的典型定义如下:
1 2 3 4 5 6 7 8 9 | |
输入模式:c_iflag
可用于c_iflag
成员的宏:
BRKINT
:当在输入行中检测到一个终止状态(连接丢失)时,产生一个中断IGNBRK
:忽略输入行中的终止状态ICRNL
:将接收到的回车符转换成为新行符IGNCR
:忽略接收到的回车符INLCR
:将接收到的新行符转换为回车符IGNPAR
:忽略奇偶校验错误的字符INPCK
:对接收到的字符执行奇偶校验PARMRK
:对奇偶校验错误做出标记ISTRIP
:将所有接收到的字符裁减为7比特IXOFF
:对输入启用软件流控IXON
:对输出启用软件流控
Tips:如果BRKINT
和IGNBRK
标志都未被设置,则输入行中的终止状态就被读取为NULL(0x00)
字符
输出模式:c_oflag
可用于c_oflag
成员的宏:
OPOST
:打开输出处理功能ONLCR
:将输出中的换行符转换为回车/换行符OCRNL
:将输出中的回车符转换为新行符ONOCR
:在第0列不输出回车符ONLRET
:不输出回车符OFILL
:发送填充字符以提供延时OFDEL
:用DEL而不是NULL
字符作为填充字符NLDLY
:新行符延迟选择CRDLY
:回车符延时选择TABDLY
:制表符延时选择BSDLY
:退格延时选择VTDLY
:垂直制表符延时选择FFDLY
:换页延时选择
Tips:如果没有设置OPOST
,则其他所有标志都被忽略。
c_oflag
的许多处理方式正好与输入模式对应,它还有几个其他标志,主要用于慢速终端,因为这些终端在处理回车符等字符时需要花费一定的时间。
控制模式:c_cflag
可用于c_cflag
成员的宏:
CLOCAL
:忽略所有调制解调器的状态行CREAD
:启用字符接收器CS5
:发送或接收字符时使用5比特CS6
:发送或接收字符时使用6比特CS7
:发送或接收字符时使用7比特CS8
:发送或接收字符时使用8比特CSTOPB
:每个字符使用两个停车位而不是一个HUPCL
:关闭挂断调制解调器PARENB
:启用奇偶校验码的生成和检测功能PARODD
:使用奇校验而不是偶校验
控制模式主要用于串行线连接调制解调器的情况。
本地模式:c_lflag
可用于c_lflag
成员的宏:
ECHO
:启用输入字符的本地回显功能ECHOE
:接收到ERASE
时执行退格、空格的工作组合ECHOK
:接收到KILL
字符时执行删除操作ECHONL
:回显新行符ICANON
:启用标准输入处理IEXTEN
:启用基于特定实现的函数ISIG
:启用信号NOFLSH
:禁止清空队列TOSTOP
:在试图进行写操作之前给后台进程发送一个信号
ECHO
的作用是抑制输入字符的回显,ICANON
是将终端在两个截然不同的接收字符处理模式间进行切换,如果设置了ICANON
,就启用标准输入,后则启用非标准模式。
特殊控制字符:c_cc
特殊控制字符是一些字符组合,如Ctrl+C
,当用户键入这样的组合键时,终端会采取一些特殊的处理方式。 根据终端是否被设置为标准模式,即termios
结构中c_lflag
成员是否设置了ICANON
标志,c_cc
数据有两种差别很大的用法。
标准模式中可以使用的数组下标:
VEOF
:EOF
字符VEOL
:EOL
字符VERASE
:ERASE
字符VINTR
:INTR
字符VKILL
:KILL
字符VQUIT
:QUIT
字符VSUSP
:SUSP
字符VSTART
:START
字符VSTOP
:STOP
字符
非标准模式中可以使用的数组下标:
VINTR
:INTR
字符VMIN
:MIN
值VQUIT
:QUIT
字符VSUSP
:SUSP
字符VTIME
:TIME
值VSTART
:START
字符VSTOP
:STOP
字符
字符
INTR
:该字符使终端驱动程序向与终端相连的进程发送SIGINT
信号QUIT
:该字符使终端驱动程序向与终端相连的进程发送SIGQUIT
信号ERASE
:该字符使终端驱动程序删除输入行中的最后一个字符KILL
:该字符使终端驱动程序删除整个输入行EOF
:该字符使终端驱动程序将输入行中的全部字符传递给正在读取输入的应用程序,如果输入行为空,read
调用将返回0
,就好像在文件结尾调用read
一样EOL
:该字符的作用类似行结束符,效果和常用的新行符相同SUSP
:该字符使终端驱动程序与终端相连的进程发送SIGSUSP
信号STOP
:该字符的作用是“截流”,即阻止向终端的进一步输出,它通常被设置为ASCII的XOFF
字符,即C-S
键START
:该字符重新启动被STOP
字符暂停的输出,它通常被设置为ASCII的XON
字符
TIME 和 MIN 值
TIME
和MIN
只能用于非标准模式,两者结合起来共同控制对输入的读取。
两者结合分为如下4种情况:
MIN = 0
和TIME = 0
:在这种情况下,read
调用总是立即返回。如果有等待处理的字符,它们会被返回;如果没有等待处理的字符,read
返回0
,并且不读取任何字符MIN = 0
和TIME > 0
:在这种情况下,只要有字符可以处理或者经过TIME
个十分之一秒的时间间隔,read
调用就返回,如果超时而未读取到任何字符,read
返回0
,否则read
返回读取的字符数目MIN > 0
和TIME = 0
:在这种情况下,read
将一直等待调用,直到有MIN
个字符可以读取时才返回读取字符的数目,到达文件结尾时返回0
MIN > 0
和TIME > 0
:当read
被调用时,它会等待接收一个字符。当接收到第一个字符及后续的每个字符后,一个字符间隔定时器就被启动(如果定时器已经在运行,则重启它)。当有MIN
个字符可读或者两个字符之间的间隔超过了TIME
个十分之一秒时,read
调用返回。这个功能可以区分是单独按下了Escape
键还是按下一个以Escape
键开始的功能组合键。Tips:网络通信或者处理器的高负载将使得类似的这样的定时器数去作用
通过 shell 访问终端模式
可以使用$ stty -a
命令来查看当前termios
的设置情况
tcgetattr 函数
tcgetattr 原型 1 2 | |
tcgetattr
作用:初始化与一个终端对应的termios
结构。这个函数调用把当前终端接口的值写入termios_p
参数指向的结构。
tcsetattr 函数
tcsetattr 原型 1 2 3 | |
tcsetattr
作用:在通过tcgetattr
函数调用后终端接口变量的值被修改了,可以用过tcsetattr
调用来重新配置终端接口。
参数actions
控制修改方式,共有三种:
TCSANOW
:立即对值进行修改TCSADRAIN
:等当前的输出完成后再对值进行修改TCSAFLUSH
:等当前的输出完成后再对值进行修改,但丢弃还未从read
调用返回的当前可用的任何输入
终端速度相关函数
函数原型 1 2 3 4 5 6 | |
Tips:这些函数作用于termios
结构而不是直接作用与端口。
其他函数
函数原型 1 2 3 4 5 | |
tcdrain
作用:让调用程序一直等待,直到所有排队的输出都已经发送完毕
tcflow
作用:用于暂停或者重新开始输出
tcflush
作用:用于清空输入、输出或者两者都清空