1. 什么是Linux 的终端I/O ,它分为几种类型。
终端是一种字符型设备,有多种类型,通常使用tty 来简称各种类型的终端设备。终端特殊设备文件一般有以下几种: 串行端口终端(/dev/ttySn ) ,伪终端(/dev/pty/ ),控制终端(/dev/tty ) ,控制台终端(/dev/ttyn, /dev/console ).
1. 串行端口终端(Serial Port Terminal )是使用计算机串行端口连接的终端设备。计算机把每个串行端口都看作是一个字符设备。串行端口所对应的设备名称是/dev/tts/0 (或/dev/ttyS0 )、/dev/tts/1 (或/dev/ttyS1 )等,设备号分别是(4,0 )、(4,1 )等,分别对应于DOS 系统下的COM1 、COM2 等。
2. 伪终端(Pseudo Terminal )是成对的逻辑终端设备,例如/dev/ptyp3 和/dev/ttyp3 (在设备文件系统中分别是/dev/pty/m3 和/dev/pty/s3 ), 它们与实际物理设备并不直接相关。对于ttyp3 (s3 ),任何设计成使用一个串行端口设备的程序都可以使用该逻辑设备。但对于使用ptyp3 的程序,则需要专门设计来使用ptyp3 (m3 )逻辑设备。 如果使用telnet 远程登录,则telnet 会开始连接到设备ptyp2 (m2 )上(一个伪终端端口上)。此时一个getty 程序就应该运行在对应的ttyp2 (s2 )端口上。当telnet 从远端获取了一个字符时,该字符就会通过m2 、s2 传递给getty 程序,而getty 程序就会通过s2 、m2 和telnet 程序往网络上返回”login: ”字符串信息。登录程序与telnet 程序就通过“伪终端”进行通信,通过使用适当的软件,就可以把两个甚至多个伪终端设备连接到同一个物理串行端口上。因为只存在16 个ttyp (ttyp0 —ttypf )的设备文件,就使用了象q 、r 、s 等字符来代替p 。例如,ttys8 和ptys8 就是一个伪终端设备对。目录/dev/pts 是一个类型为devpts 的文件系统,并且可以在被加载文件系统列表中看到。虽然/dev/pts 象是设备文件系统中的一项,但它其实是一种不同的文件系统。
3. 如果当前进程有控制终端(Controlling Terminal )的话,那么/dev/tty 就是当前进程的控制终端的设备特殊文件。可以使用命令”ps –ax ”来查看进程与哪个控制终端相连使用命令”tty ”可以查看它具体对应哪个实际终端设备。/dev/tty 有些类似于到实际所使用终端设备的一个联接。
4. Linux 系统中,计算机显示器通常被称为控制台终端(Console ), 它仿真了类型为Linux 的一种终端(TERM=Linux ),并且有一些设备特殊文件与之相关联:tty0 、tty1 、tty2 等。当你在控制台上登录时,使用的是tty1 。使用Alt+[F1 —F6] 组合键时,我们就可以切换到tty2 、tty3 等上面去。tty1 –tty6 等称为虚拟终端,而tty0 则是当前所使用虚拟终端的一个别名,系统所产生的信息会发送到该终端上。只有系统或超级用户root 可以向/dev/tty0 进行写操作 .
终端IO有两种模式:
1. 规范模式。在这种模式下,输入以行为单位进行处理。每次读请求终端驱动都返回一行。
2. 非规范模式。输入字符不编成一行。
默认情况下系统采用规范模式。
POSIX。1定义了 11种特殊输入字符,它们中的9种可以改变。
<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
};
输入标志:控制从终端设置驱动程序输入的字符。
输出标志:控制输出字符
控制标志:控制标志影响RS-232 串行线
本地标志:本地标志影响驱动与用户之间的接口。如开关回显,显示擦除字符,使终端产生信号,为后台输出的工作控制停止信号
Tcflag_t 对于标志位值已经足够大了,可以定义它为 unsigned int 或 unsigned long 类型。
C_cc 数组包含所有我们能改变的特别字符。Cc_t 通常定义为unsigned char
控制字符:
CLOCAL: 忽略modem 状态线
CREAD: 使 接收器中 开启
CRTSCTS: 使硬件流控制开启
CSIZE: 字符大小的掩码
CSTOPB: 发送两个stop 位,其它情况发送一个
CUPCL: 最后关闭时停止
PARENB: 部分开启
PARODD: 奇数,其它为偶数
输入标志:
BRKINT: 当BREAK 时产生SIGINT 信号
ICRNL: 在输入时将CR 转成 NL
IGNBRK : 忽略BREAK
IGNCR: 忽略CR
IGNPAR: 忽略字符
IMAXBEL 当队列满时振玲
INLCR 将NL 换成CR
INPCK
IUCLC 大写字符转成小写字符
IXANY
IXOFF 开始或关闭输入流控制
IXON
PARMRK
本地标志:
ECHO 开启 echo
ECHOCTL 回显控制字符
ECHOE 显示擦除字符
ECHOK 显示KILL
ECHOKE 为KILL 显示擦除字符
ECHONL 显示NL
ECHOPRT 为硬拷贝显示擦除字符
EXTPROC 扩展字符
FLUSHO
ICANON 规范输入
IEXTEN
ISIG 使终端产生信号开启
NOFLSH
……
终端IO 总结:
Tcgetattr 得到属性(termios 结构)
Tcsetattr
Cfgetispeed get input speed
Cfgetospeed 得到输出速度
Cfsetispeed
Cfsetospeed
Tcdrain 等待所有输出传出
Tcflow 挂起传输或接收
Tcflush
Tcsendbreak 发送BREAK 字符
Tcgetpgrp 得到前台进程组ID
Tcsetpgrp 设置前台组ID
Tcgetsid 得到控制session 头,进程组ID
每个终端设备有一个输入队列和一个输出队列
得到或调置终端属性
有两个函数很重要:tcgetattr 和 tcsetattr,它们都包含在<termios.h>头文件中。原型如下 int tcgetattr(int filedes, struct termios *termptr); 和 int tcsetattr(int filedes, int opt, const struct termios *termptr);
opt 的值可以有以下几个:
TCSANOW (terminal control set attribute now),改变立即发生
TCSADRIN ,在所有输出已经传出后,发生改变。
TCSAFLUSH , 所有输出已经传出后,且当改变发生时,所有没有输入被读的数据丢弃。
终端标识
历史上大多数版本的UNIX控制终端使用 /dev/tty. POSIX.1提供了我们能调用决定控
制终端名子的运行时函数。
#include <stdio.h>
char *ctermid(char *ptr);
如果ptr非空,它指向 L_ctermid 数组的字节,且控制终端的名子存放在该数组中。如果ptr是空指针,函数为数组分配空间,进程控制终端的名子存放在该数组中。
isatty函数,当文件描述符指向的是终端设备,则返回真值。
ttyname函数,它返回终端设备的路径名。
规范模式
规范终端IO很简单,我们发出一个读请求后,终端驱动程序读取一行信息后返回,有几个条件引起读返回:
1. 当读到请求的字节数时读请求返回。
2. 当读请求遇到定界符时,读请求返回。如NL,EOL,EOL2, EOF
3. 如果捕获到一个信号且函数不自动重启,则读请求探返回。
非规范模式
非规范模式是指关闭termios结构中的c_lflag域的ICANON标志位。在非规范模式下,输入数据不编进一行,下面的一些特殊字符也不进行处 理:ERASSE, KILL, EOF,NL, EOL, EOL2, CR, REPRINT ,STATUS 和 WERASE。
规范模式是容易的,系统每次返回一行。但在非规范模式下,系统知道什么时候返回数据给我们呢?
解决办法是系统或是在指定数量已经读够或是给定的时间超时时返回。该技术使用了两个变量:MIN和TIME。
MIN : 指明了在读返回之前应读的最小字节数。
TIME : 指明了等待数据的时间
它们一共有四种组合,每种情况描述如下:
1. MIN >0, TIME>0
TIMER指明从第一个接收到的字节开始计数。如果在超时前,接收到MIN个字节,那么返回字节数。如果发生了时间超时,至少有一个字节返回,因为计时器是从接收到第一个字节开始计数的。
2. MIN >0, TIME == 0
在这种情况下,直到读到的字节数达到MIN个时才返回。
3. MIN==0, TIME >0
TIME的值从读用read开始计算,如果收到一个字节或时间超时时返回。
4. MIN==0, TIME==0
如果一些数据是有效的,则 read返回这些数据。如果没有数据有效,则立即返回。
终端窗口大小
内核为每个终端和伪终端维护着一个winsize结构:
struct winsize{
unsigned short ws_row;
unsigned short ws_col;
unsigned short ws_xpixel;
unsigned short ws_ypixel;
};
规则如下:
1. 我们可以使用 ioctl 的 TIOCGWINSZ 得到该结构的当前值。
2. 我们可以使用 ioctl 的 TIOCSWINSZ 将新值保存到内核维护的结构中。如果这个新值与当前内核中存放的值不一样,则SIGWINCH信号发送给当前进程组。
3. 当值发生变化时,除了保存结构的当前值和产生一个信号之外,内核不做其它事情。
terncap, terminfo, curses
termcap 代表 terminal capability, 它表示 /etc/termcap文件和读该文件的一列列程。termcap文件中包含各种终端的描述。如终端支持那种特性和使终端执行某些操作。