《unix高级环境编程》终端 I/O——终端 IO 基本概述
来源:互联网 发布:linux退出vi模式 编辑:程序博客网 时间:2024/04/30 03:08
终端基本概念
终端 IO 是一种字符型设备,终端特殊设备文件一般有以下几种:
- 串行端口终端:是使用计算机串行端口连接的设备,计算机把每个串行端口都看作是一个字符设备。串行端口所对应的设备名称 /dev/ttySn(n表示从0开始的整数);
- 伪终端:是成对的逻辑终端设备,例如 /dev/ptyp3 和/ dev/ttyp3(在设备文件系统中分别是 /dev/pty/m3 和/ dev/pty/s3 ),它们与实际物理设备并不直接相关;
- 控制终端:是当前进程的控制终端的设备特殊文件 /dev/tty。可以使用命令”ps –ax”来查看进程与哪个控制终端相连使用命令”tty”可以查看它具体对应哪个实际终端设备。/dev/tty有些类似于到实际所使用终端设备的一个联接;
- 控制台终端:计算机显示器通常被称为控制台终端(Console),它仿真了类型为Linux的一种终端(TERM=Linux),并且有一些设备特殊文件与之相关联:tty0、tty1、tty2等;
终端 IO 有两种不同的工作模式:
- 规范模式输入处理:在这种模式下,终端输入以行为单位进行处理,对于每个读要求,终端驱动程序最多返回一行;
- 非规范模式输入处理:输入字符不以行为单位进行处理;
终端设备是由一般位于内核中的终端驱动程序控制,每个终端设备有一个输入队列和一个输出队列,如下图所示:
大多数 UNIX 系统在一个称为终端行规程的模块中进行规范处理。它位于内核通用读、写函数和实际设备驱动程序之间的模块,如下图所示:
操作终端的结构定义如下:
/* 终端IO *//* 终端IO的数据结构 */#include <termios.h>struct termios{ tcflag_t c_iflag; /* input flag */ tcflag_t c_oflag; /* output flag */ tcflag_t c_cflag; /* control flag */ tcflag_t c_lflag; /* local flag */ cc_t c_cc[NCCS]; /* control characters */};
该数据结构的输入标志由终端设备驱动程序用来控制字符的输入(剥除输入字节的第8位,允许输入奇偶校验等);输出标志则控制终端设备驱动的输出(执行输出处理、将换行符映射为 CR/LF 等);控制标志影响到 RS-232串行线(忽略调制解调器的状态线、每个字符的一个或两个停止位等);本地标志影响驱动程序和用户之间的接口(回送的开或关、可视的擦除字符、终端产生的信号的启用以及后台输出的作业控制停止信号等);有关各个标志的取值可以参考书上的表格。
特殊输入字符
终端设备操作函数
以下先给出终端的操作函数:
/*_______________________________________ * 函数 说明 *_______________________________________ * tcgetattr 获取属性 * tcsetattr 设置属性 *_______________________________________ * cfgetispeed 得到输入速度 * cfgetospeed 得到输出速度 * cfsetispeed 设置输入速度 * cfsetospeed 设置输出速度 *_______________________________________ * tcdrain 等待所有输出都被传输 * tcflow 挂起传输或接收 * tcflush 刷清未决输入和/或输出 * tcsendbreak 送BREAK字符 *_______________________________________ * tcgetpgrp 得到前台进程组ID * tcsetpgrp 设置前台进程组ID * tcgetsid 得到控制TTY的会话首进程的进程组ID *_______________________________________ * */
针对这些终端操作函数进行分类讨论:
终端属性
可以通过以下两个函数获取和设置终端属性,该属性结构是 termios 结构,有了终端属性方便进一步的操作,其定义如下:
/* * 函数功能:获取和设置终端属性; * 返回值:若成功则返回0,出错则返回-1; * 函数原型: */#include <termios.h>int tcgetattr(int filedes, struct termios *termptr);/* 获取终端属性 */int tcsetattr(int filedes, int opt, const struct termios *termptr);/* 设置终端属性 *//* * 说明: * filed是终端设备描述符; * * opt参数可以指定为以下的值: * TCSANOW 更改立即生效; * TCSADRAIN 发送所有输出后更改才发生,若更改输出参数则应该使用此选项; * TCSAFLUSH 发送所有输出后更改才发生,更进一步,在更改发生时未读的所有输入数据都被删除; */
测试程序:
#include "apue.h"#include <termios.h>int main(){ struct termios term; //获取termios结构 if(tcgetattr(STDIN_FILENO,&term) < 0) err_sys("tcgetattr error"); switch(term.c_cflag & CSIZE) { case CS5: printf("5 bits/byte\n"); break; case CS6: printf("6 bits/byte\n"); break; case CS7: printf("7 bits/byte\n"); break; case CS8: printf("8 bits/byte\n"); break; default: printf("Unknown bityes/byte\n"); } term.c_cflag &= ~CSIZE; //字符长度清0 term.c_cflag |= CS5; //设置为8 bites/byte if(tcsetattr(STDIN_FILENO,TCSANOW,&term) < 0) err_sys("tcsetattr error"); exit(0);}输出结果:
8 bits/byte
波特率函数
/* * 函数功能:获取和设置终端波特率; * 函数原型: */#include <termios.h>/* 返回值:若成功则返回波特率值 */speed_t cfgetispeed(const struct termios *termptr);/* 获取输入波特率 */speed_t cfgetospeed(const struct termios *termptr);/* 获取输出波特率 *//* 返回值:若成功则返回0,出错则返回-1;*/int cfsetispeed(struct termios *termptr, speed_t speed);/* 设置输入波特率 */int cfsetospeed(struct termios *termptr, speed_t speed);/* 设置输出波特率 */
行控制函数
/* * 函数功能:终端设备的行控制; * 返回值:若成功则返回0,若出错则返回-1; * 函数原型: */#include <termios.h>int tcdrain(int filedes);int tcflow(int filedes, int action);int tcflush(int filedes, int queue);int tcsendbreak(int filedes, int duration);/* * 说明: * action参数取值如下: * TCOOFF 输出被挂起; * TCOON 重新启动以前被挂起的输出; * TCIOFF 系统发送一个STOP字符,将使终端设备暂停发送数据; * TCION 系统发送一个START字符,将使终端设备恢复发送数据; * * queue参数取值如下: * TCIFUSH 刷清输入队列; * TCOFUSH 刷清输出队列; * TCIOFUSH 刷清输入、输出队列; */
终端标识
下面是一些对控制终端操作的函数,其定义如下:
/* 终端标识 *//* * 函数功能:确定控制终端的名字; * 返回值:若成功则返回指向控制终端名的指针,出错则返回指向空字符串的指针; * 函数原型: */#include <stdio.h>char * ctermid(char *ptr);/* * 说明: * ptr非null,且指向长度至少为L_ctermid字节的数组,进程的控制终端名存放在该数组中; * ptr为null,则该函数为数组分配空间,进程的控制终端名也放在该数组中; *//* * 函数功能:控制终端的操作; * 函数原型: */#include <unistd.h>int isatty(int filedes);/* 返回值:若为终端设备则返回1(真),否则返回0(假)*/char *ttyname(int filedes);/* 返回值:指向终端路径名的指针,若出错则返回NULL */下面程序是测试控制终端的信息:
#include "apue.h"#include <termios.h>static char ctermid_name[L_ctermid];char *Mctermid(char *str){ if(str == NULL) str = ctermid_name; return (strcpy(str,"/dev/tty"));}int main(){ char tername[50]; char *name; ctermid(tername); printf("terminate name is: %s\n", tername); Mctermid(tername); printf("Mterminate name is: %s\n", tername); printf("Test isatty...\n"); printf("fd 0 is: %s\n",isatty(0)? "tty" : "not a tty"); printf("fd 1 is: %s\n",isatty(1)? "tty" : "not a tty"); printf("fd 2 is: %s\n",isatty(2)? "tty" : "not a tty"); printf("Test ttyname...\n"); if(isatty(0)) { name = ttyname(0); if(name == NULL) name ="undefined"; } else name = "not a tty"; printf("fd 0 :%s\n", name); if(isatty(1)) { name = ttyname(1); if(name == NULL) name ="undefined"; } else name = "not a tty"; printf("fd 1 :%s\n",name); if(isatty(2)) { name = ttyname(2); if(name == NULL) name ="undefined"; } else name = "not a tty"; printf("fd 2 :%s\n",name); exit(0);}输出结果:
./tty < /dev/console 2> /dev/nullterminate name is: /dev/ttyMterminate name is: /dev/ttyTest isatty...fd 0 is: ttyfd 1 is: ttyfd 2 is: not a ttyTest ttyname...fd 0 :/dev/consolefd 1 :/dev/pts/1fd 2 :not a tty
终端的窗口大小
内核为每个终端和伪终端保存了一个窗口大小结构 winszie,
- 用 ioctl 函数的 TIOCGWINSZ 命令可以获取此结构的当前值;
- 也可以通过 ioctl 的 TIOCSWINSZ 命令可以将此结构体的新值存放到内核中,如果新值与存放在内核中的当前值不同,则会向前台进程组发送 SIGWINCH 信号,系统对此信号的默认处理是忽略该信号;
- 当前终端窗口大小发生变化时,也会产生 SIGWINCH 信号;
/* 终端的窗口大小 *//* 窗口大小的结构体 */struct winsize{ unsigned short ws_row; /* row, in characters */ unsigned short ws_col; /* columns, in characters */ unsigned short ws_xpixel; /* horizontal size, pixels (unused) */ unsigned short ws_ypixel; /* vertical size, pixels (unused) */};下面程序实现打印当前终端的窗口大小:
#include "apue.h"#include <termios.h>#include <sys/ioctl.h>#include <signal.h>static void pr_winsize(int fd){ struct winsize size; if(ioctl(fd,TIOCGWINSZ,(char *)&size) < 0) err_sys("ioctl error"); printf("%d rows, %d columns\n", size.ws_row, size.ws_col);}static void sig_winch(int signo){ printf("SIGWINCH received\n"); pr_winsize(STDIN_FILENO);}int main(){ if(isatty(STDIN_FILENO) == 0) { printf("STDIN_FILENO is not terminate device.\n"); exit(1); } if(signal(SIGWINCH, sig_winch) == SIG_ERR) err_sys("signal error"); pr_winsize(STDIN_FILENO); for( ; ;) pause();}输出结果:
/* 改变终端的窗口大小时会发送信号 SIGWINCH */24 rows, 80 columnsSIGWINCH received24 rows, 79 columns^C
参考资料:
《UNIX高级环境编程》
2 0
- 《unix高级环境编程》终端 I/O——终端 IO 基本概述
- 《unix高级环境编程》终端 I/O——终端 IO 基本概述
- UNIX环境高级编程-第18章- 终端 I/O
- UNIX环境高级编程 第十八章 终端I/O 总结
- 《UNIX环境高级编程》十八终端I/O读书笔记
- 终端IO--unix环境高级编程读书笔记
- 《UNIX环境高级编程》笔记--终端IO
- unix环境终端I/O
- UNIX环境高级编程读书笔记(十一)—终端IO (1)
- UNIX环境高级编程读书笔记(十一)—终端IO (2)
- UNIX环境高级编程读书笔记(十一)—终端IO (1)
- UNIX环境高级编程读书笔记(十一)—终端IO (2)
- UNIX环境高级编程读书笔记(十一)—终端IO (1)
- UNIX环境高级编程读书笔记(十一)—终端IO (2)
- UNIX环境编程 终端IO
- UNIX环境高级编程——文件I/O
- UNIX环境高级编程——标准I/O库
- UNIX环境高级编程——标准I/O库
- Linux -- logrotate 切割 Nginx
- 贝塞尔曲线
- 左旋字符串的一种野路子解法,时间复杂度O(2N)
- 触发器详解
- 黑马程序员——类的本质
- 《unix高级环境编程》终端 I/O——终端 IO 基本概述
- 叠片过滤器:怎样解决输油管和过滤器堵塞问题
- 叠片过滤器:过滤器设备的普遍使用
- 对象继承机制
- thinkphp中的查询技巧总结
- C#获得刚刚插入记录的自增ID
- Nginx RTMP 功能研究
- CentOS 6.6安装Matlab R2014a UNIX版
- 图说Java —— 理解Java机制最受欢迎的8幅图