Linux程序设计:终端

Linux user 没有不和终端打交道的,所以做 Linux 下的开发,必然会涉及到终端的操作。

终端分为标准模式非标准模式

默认状态下,只有在用户按下回车键后,程序才能读到终端的输入,这种处理方式被成为标准模式standard mode),所有的输入都是基于行处理,在一个输入行完成之前(通常是用户按下回车键之前),应用程序读不到用户输入的任何字符。

非标准模式则可以与标准模式相对应,在用户按下回车键前,读取数据到程序。

以下是一些与终端操作相关的函数

isatty 函数

isatty 原型
1
2
3
#include<unistd.h>  int isatty(int fd); 

isatty作用:检查标准输出是否被重定向,实质是检查底层文件描述符是否关联到了一个终端而已。

如果打开的文件描述符fd连接到了一个终端,则系统调用isatty返回1,否则返回0

isatty只能对文件描述符进行操作,所以需要与fileno函数结合使用。

example.c
1
2
3
4
5
6
7
8
9
10
11
12
13
#include<unistd.h> #include<stdio.h> #include<stdlib.h> int main() {  if (!isatty(fileno(stdout)))  {  fprintf(stderr, "You are not a terminal!\n");  exit(1);  }  fprintf(stdout, "Hello, World!\n");  exit(0); } 
运行结果
1
2
3
4
$ ./example Hello, World! $ ./example > temp You are not a terminal! 

termios 结构

termios数据结构和相关函数调用都定义在头文件termios.hcurses.h。 最小化的termios结构的典型定义如下:

termios 结构
1
2
3
4
5
6
7
8
9
#include<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]; }; 

输入模式:c_iflag

可用于c_iflag成员的宏:

  • BRKINT:当在输入行中检测到一个终止状态(连接丢失)时,产生一个中断
  • IGNBRK:忽略输入行中的终止状态
  • ICRNL:将接收到的回车符转换成为新行符
  • IGNCR:忽略接收到的回车符
  • INLCR:将接收到的新行符转换为回车符
  • IGNPAR:忽略奇偶校验错误的字符
  • INPCK:对接收到的字符执行奇偶校验
  • PARMRK:对奇偶校验错误做出标记
  • ISTRIP:将所有接收到的字符裁减为7比特
  • IXOFF:对输入启用软件流控
  • IXON:对输出启用软件流控

Tips:如果BRKINTIGNBRK标志都未被设置,则输入行中的终止状态就被读取为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:该字符的作用是“截流”,即阻止向终端的进一步输出,它通常被设置为ASCIIXOFF字符,即C-S
  • START:该字符重新启动被STOP字符暂停的输出,它通常被设置为ASCIIXON字符

TIME 和 MIN 值

TIMEMIN只能用于非标准模式,两者结合起来共同控制对输入的读取。

两者结合分为如下4种情况:

  • MIN = 0TIME = 0:在这种情况下,read调用总是立即返回。如果有等待处理的字符,它们会被返回;如果没有等待处理的字符,read返回0,并且不读取任何字符
  • MIN = 0TIME > 0:在这种情况下,只要有字符可以处理或者经过TIME个十分之一秒的时间间隔,read调用就返回,如果超时而未读取到任何字符,read返回0,否则read返回读取的字符数目
  • MIN > 0TIME = 0:在这种情况下,read将一直等待调用,直到有MIN个字符可以读取时才返回读取字符的数目,到达文件结尾时返回0
  • MIN > 0TIME > 0:当read被调用时,它会等待接收一个字符。当接收到第一个字符及后续的每个字符后,一个字符间隔定时器就被启动(如果定时器已经在运行,则重启它)。当有MIN个字符可读或者两个字符之间的间隔超过了TIME个十分之一秒时,read调用返回。这个功能可以区分是单独按下了Escape键还是按下一个以Escape键开始的功能组合键。Tips:网络通信或者处理器的高负载将使得类似的这样的定时器数去作用

通过 shell 访问终端模式

可以使用$ stty -a命令来查看当前termios的设置情况

tcgetattr 函数

tcgetattr 原型
1
2
#include<termios.h> int tcgetattr(int fd, struct termios *termios_p); 

tcgetattr作用:初始化与一个终端对应的termios结构。这个函数调用把当前终端接口的值写入termios_p参数指向的结构。

tcsetattr 函数

tcsetattr 原型
1
2
3
#include<termios.h>  int tcsetattr(int fd, int actions, const struct termios *termios_p); 

tcsetattr作用:在通过tcgetattr函数调用后终端接口变量的值被修改了,可以用过tcsetattr调用来重新配置终端接口。

参数actions控制修改方式,共有三种:

  • TCSANOW:立即对值进行修改
  • TCSADRAIN:等当前的输出完成后再对值进行修改
  • TCSAFLUSH:等当前的输出完成后再对值进行修改,但丢弃还未从read调用返回的当前可用的任何输入

终端速度相关函数

函数原型
1
2
3
4
5
6
#include<termios.h>  speed_t cfgetispeed(const struct termios *); speed_t cfgetospeed(const struct termios *); int cfsetispeed(struct termios *, speed_t speed); int cfsetospeed(struct termios *, speed_t speed); 

Tips:这些函数作用于termios结构而不是直接作用与端口。

其他函数

函数原型
1
2
3
4
5
#include<termios.h>  int tcdrain(int fd); int tcflow(int fd, int flowtype); int tcflush(int fd, int in_out_selector); 

tcdrain作用:让调用程序一直等待,直到所有排队的输出都已经发送完毕

tcflow作用:用于暂停或者重新开始输出

tcflush作用:用于清空输入、输出或者两者都清空

你可能感兴趣的:(c,linux,struct,kill,终端,Terminal)