当前位置: 首页 > news >正文

终端I/O.

终端I/O

  • 一、综述
    • 二、特殊输入字符
      • 三、获得和设置终端属性
      • 四、stty命令
      • 五、波特率函数
      • 六、行控制函数
      • 七、终端标识
      • 八、规范模式与非规范模式
  • 1.加工模式、cbreak模式以及原始模式
      • 九、终端窗口大小
      • 十、termcap、terminfo和curses

一、综述

终端I/O有两种不同的工作模式:

  1. 规范模式输入处理。在这种模式中,对终端输入以行为单位进行处理。对于每个读请求,终端驱动程序最多返回一行。

  2. 非规范模式输入处理。输入字符不装配成行。

POSIX.1定义了11个特殊字符,其中9个可以更改。例如文件结束符(Ctrl+D)和挂起字符(Ctrl+Z)。

终端设备是由通常位于内核中的终端驱动程序控制的。每个终端设备都有一个输入队列和一个输出队列,如下图所示:
-

许多UNIX系统在一个称为终端行规程的模块 中进行全部的规范处理。可以将这个模块设想成一个盒子,位于内核通用读、 写函数和实际设备驱动程序之间,如下图所示:

在这里插入图片描述
终端设备特性包含在头文件为<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_cc数组包含了所有可以更改的特殊字符,NCCS是该数组中元素的数量(15~20)
//cc_t类型的长度足以保存每个特殊字符,典型的是unsigned char。
//类型tcflag_t的长度足以保存每个标志值,它经常被定义为unsigned int或者 unsigned long。

输入标志通过终端设备驱动程序控制字符的输入,输出标志则控制驱动程序输出,控制标志影响RS-232串行线,本地标志影响驱动程序和用户之间的接口。

更改影响终端设备特性的标志如下所示:

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

对终端设备进行操作的各个函数:

在这里插入图片描述
函数关系示意图:
在这里插入图片描述

二、特殊输入字符

POSIX.1定义了11个在输入时要特殊处理的字符,如下图所示:

在这里插入图片描述
在这里插入图片描述

  1. 9个字符的值可以任意更改。

  2. 不能更改的两个特殊字符是换行符和回车符(分别是\n和\r),也可能是STOP和 START字符(依赖于实现)。

  3. 更改只需要修改 termios 结构中 c_cc 数组的相应项。该数组中的元素都用名字作为下标进行引用,每个名字都以字母V 开头。

  4. 禁止使用这些字符,只虚将c_cc数组中的某项设置为 _POSIX_VDISABLE的值,则禁止使用相应特殊字符。

三、获得和设置终端属性

获得和设置termios结构,调用tcgetattr和tcsetattr函数,可以 检测和修改各种终端选项标志和特殊字符,使终端按我们所希望的方式进行操作。

#include <termios.h> 
int tcgetattr(int fd, struct termios *termptr); 
int tcsetattr(int fd, int opt, const struct termios *termptr); 
//termptr:指向termios结构的指针,返回当前终端的属性,或者设置该终端的属性。
//若fd没有引用终端设备则出错返回-1,errno设置为ENOTTY。
//两个函数的返回值:若成功,返回0;若出错,返回-1

opt指定什么时候新的终端属性才起作用:

  1. TCSANOW 更改立即发生。

  2. TCSADRAIN 发送了所有输出后更改才发生。若更改输出参数则应使用此 选项。

  3. TCSAFLUSH 发送了所有输出后更改才发生。更进一步,在更改发生时未 读的所有输入数据都被丢弃(冲洗)。

  • tcsetattr函数执行了任意一种所要求的动作,即使未能执行所有要求的动作,也返回OK(表示成功。若该函数返回OK,需要检查该函数是否执行了所有要求的动作,意味着,在调用tcsetattr设置所希望的属性后,需调用tcgetattr,然后将实际终端属性与所希望的属性相比较,以检测两者是否有区别。

    • 终端第一次被打开时,其属性视具体情况而定。一些系统可能会将终端 属性初始化为具体实现所定义的值,另一些系统可能会保留并使用最后一次使 用终端时的属性值。

      • 通过打开一个带有O_TTY_INIT标志的驱动设 备,可以确认终端的行为是否遵循标准,在调用tcgetattr 时,确保初始化termios结构中的任何非标准部分,使得在修改属性和调用tcgetattr时,终端的 表现符合预期。

四、stty命令

使用stty命令进行检查和更改所有属性,stty -a显示终端的所有选项:
在这里插入图片描述

若在选项名前有一个连字符,表示该选项禁用。最后4行显示各终端特殊字符的当前设置。第1行显示当前终端窗口的行数和列数。

五、波特率函数

波特率指的是“位/ 秒”,大多数终端设备对输入和输出使用同一波特率,但是只要硬件许可,可以将它们设置为两个不同值。

#include <termios.h>
 speed_t cfgetispeed(const struct termios *termptr); 
 speed_t cfgetospeed(const struct termios *termptr); 
// 两个函数的返回值:波特率值 
 int cfsetispeed(struct termios *termptr, speed_t speed); 
 int cfsetospeed(struct termios *termptr, speed_t speed);
 // 两个函数的返回值:若成功,返回0;出错,返回-1
  1. 两个cfget函数的返回值,以及两个cfset函数的speed参数都是下列常量之一:B50、B75、B110、B134、B150、B200、B300、B600、B1200、B1800、 B2400、B4800、B9600、B19200或B38400

  2. 常量B0表示“挂断”。在调用tcsetattr 时,如若将输出波特率指定为B0,则调制解调器的控制线就不再起作用。

  3. 大多数系统定义了另外的波特率值,如B57600以及B115250

  4. 使用这些函数时,必须认识到输入、输出波特率是存储在设备的termios结构中的。在调用两个cfget函数中的任意一个之前,要先用 tcgetattr获得设备的termios结构。

  5. 在调用两个cfset函数中的任意一个 之后,要做的就是在termios结构中设置波特率。

更改影响到设备,应 当调用tcsetattr函数。即使所设置的两个波特率中的任意一个出错,在调用 tcsetattr之前可能也不会发现这个错误。

六、行控制函数

下列函数提供了终端设备的控制能力。

#include <termios.h> 
int tcdrain(int fd); 
int tcflow(int fd, int action);
int tcflush(int fd, int queue); 
int tcsendbreak(int fd, int duration);
//要求参数fd引用一 个终端设备,否则出错返回-1,errno设置为ENOTTY。
// 4个函数的返回值:若成功,返回0;若出错,返回-1
  • tcdrain 函数等待所有输出都被传递。
    • tcflow 函数用于对输入和输出流控制 进行控制。
      action参数必定是下列4个值之一:
      1. TCOOFF 输出被挂起。
      2. TCOON 重新启动以前被挂起的输出。
      3. TCIOFF 系统发送一个STOP字符,这将使终端设备停止发送数据。
      4. TCION 系统发送一个START字符,这将使终端设备恢复发送数据。

      • tcflush函数冲洗(抛弃)输入缓冲区(其中的数据是终端驱动程序已接收 到,但用户程序尚未读取的)或输出缓冲区(其中的数据是用户程序已经写 入,但尚未被传递的)。
        queue参数必定是下列3个常量之一:
        1. TCIFLUSH 冲洗输入队列。
        2. TCOFLUSH 冲洗输出队列。
        3. TCIOFLUSH 冲洗输入队列和输出队列。

      • tcsendbreak函数在指定的时间区间内发送连续的0值位流。若duration参数为0,则此种传递延续0.25~0.5秒。POSIX.1说明若duration非0,则传递时间 依赖于实现。

七、终端标识

控制终端的名字一直是/dev/ttyPOSIX.1提供了一个运行时函数,可用来确定控制终端的名字。

#include <stdio.h>
 char *ctermid(char *ptr); 
 
//返回值:若成功,返回指向控制终端名的指针;若出错,返回指向空字符串的指针
  1. 若ptr非空,代表个指针,指向长度至少为 L_ctermid (stdio.h定义)字节的数组,进程的控制终端名存储在该数组中。

  2. 若ptr是空指针,则该函数为数组(通常作为静态变量)分配空间,进程的控制终端名存储在该数组中。

  3. 这两种情况中,该数组的起始地址都被作为函数值返回。

  4. 无法确定调用者的缓冲区大小,所以也就不能防止过度使 用该缓冲区。

#include <unistd.h>
//如果文件描 述符引用一个终端设备,则isatty返回真。
 int isatty(int fd);
 //返回值:若为终端设备,返回1(真);否则,返回0(假) 

//ttyname返回的是在该文件描述符上打开的终端设备的路径名。
 char *ttyname(int fd); 
 //返回值:指向终端路径名的指针;若出错,返回NULL

八、规范模式与非规范模式

规范模式:

  • 通过设定ICANON标志打开规范模式输入。

    • 发一个读请求,当一行已经输入后,终端驱动程序即返
      区分规范描述符输入:
  1. 输入被装配成行,通过如下几种行结束符终结:NL、EOL、EOL2、EOF或者CR

  2. 打开了行编辑功能。可以修改当前行中的输入。像ERASE、KILL可用。设定IEXTEN标志,WEASE也可用。

  3. 若设定IEXTEN标志,则REPRINT和LNEXT字符都可用。

  4. 规范模式存在一行完整输入,终端的read()才返回。

非规范模式:

  • 应用程序在用户没有提供终止符时也需从终端中读取字符,非规范模式用于此。

    • 通过关闭termios结构中c_lflag字段的ICANON标志来指定非规范模式。

      • 非规范模式不会处理特殊处理。

TIME(十分之一秒为单位指定超时时间)和MIN(指定被读取字节数的最小值),若read满足下列要求会返回可用字节数和所请求的字节数中较小的哪个值。

  1. MIN0,TIME0(轮询读取)。

  2. MIN>0,TIME==0(阻塞式读取)。

  3. MIN==0,TIME>0(带有超时机制的读操作)。

  4. MIN>0,TIME>0(既有超时机制又有最小读取字节数的要求)。

1.加工模式、cbreak模式以及原始模式

中断驱动程序能够以这3三种方式处理输入,如图所示三种区别。
在这里插入图片描述

九、终端窗口大小

大多数UNIX系统都提供了一种跟踪当前终端窗口大小的方法,在窗口大小 发生变化时,使内核通知前台进程组。内核为每个终端和伪终端都维护了一个 winsize结构:

struct winsize {
 unsigned short ws_row; 
 unsigned short ws_col;  
 unsigned short ws_xpixel; 
 unsigned short ws_ypixel; 
 };

此结构的规则如下:

  1. ioctlTIOCGWINSZ命令可以取此结构的当前值。

  2. ioctlTIOCSWINSZ 命令可以将此结构的新值存储到内核中。果此 新值与存储在内核中的当前值不同,则前台进程组会收到SIGWINCH信号。

  3. 除了存储此结构的当前值以及在此值改变时产生一个信号以外,内核对该 结构不进行任何其他操作。对结构中的值进行解释完全是应用程序的工作。

提供这种功能的目的是,当窗口大小发生变化时应用程序能够得到通知 (如vi编辑器)。应用程序接收此信号后,可以获取窗口大小的新值,然后重 绘屏幕。

例:打印当前窗口大小,然后休眠。窗口改变时,程序捕捉SIGWINCH信号,然后打印新的大小。

#include "apue.h"
#include <termios.h>
#ifndef	TIOCGWINSZ
#include <sys/ioctl.h>
#endif

static void pr_winsize(int fd)
{
	struct winsize	size;

	if (ioctl(fd, TIOCGWINSZ, (char *) &size) < 0)
		err_sys("TIOCGWINSZ 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(void)
{
	if (isatty(STDIN_FILENO) == 0)
		exit(1);
	if (signal(SIGWINCH, sig_winch) == SIG_ERR)
		err_sys("signal error");
		pr_winsize(STDIN_FILENO);	/* print initial size */
	for ( ; ; )					/* and sleep forever */
		pause();
}

十、termcap、terminfo和curses

  1. termcap:是终端能力,它涉及文本文件/etc/termcap 和一套读此文件的例程,包含对各种终端说明。

  2. terminfo:terminfo库中,终端说明基本上都是文本说明 的编译版本,在运行时易于被快速定位。

  3. curses:curses库提供了很多函数,用来设置 原始模式、设置cbreak模式、打开和关闭回显等。

相关文章:

  • MySQL触发器简介
  • 计算机SSM毕设推荐 40个高质量软件工程毕设项目分享【源码+论文】(四)
  • C语言利用函数解决问题:1.实现reverse函数完成数组元素的逆置;2.将两个数组的元素进行互换;3.统计二进制中1的个数
  • Html:网站设计的内容概览简介、网页设计流程/工具/内容组成、脚本代码之详细攻略
  • 网络安全笔记 -- XXEXML(利用、检测、绕过)
  • c语言小项目(静态通讯录)
  • 数据结构与算法——左程云09
  • C++异常
  • 【ASM】字节码操作 如何使用 visitFrame
  • 【Spring Boot 集成应用】 OAUTH2统一认证单点登录中的各种模式说明
  • 数据链路层的检错技术——循环冗余校验CRC(Cyclic Redundancy Check)
  • 【C++】IO流
  • Pytorch理解
  • 区块链中的隐私保护技术
  • postgres 源码解析 元组插入流程 Heap_Insert
  • [case10]使用RSQL实现端到端的动态查询
  • [笔记] php常见简单功能及函数
  • Docker 笔记(2):Dockerfile
  • Git学习与使用心得(1)—— 初始化
  • idea + plantuml 画流程图
  • IE报vuex requires a Promise polyfill in this browser问题解决
  • IOS评论框不贴底(ios12新bug)
  • java多线程
  • mac修复ab及siege安装
  • PAT A1050
  • Promise初体验
  • Redash本地开发环境搭建
  • SpiderData 2019年2月23日 DApp数据排行榜
  • Web Storage相关
  • webpack4 一点通
  • Windows Containers 大冒险: 容器网络
  • 创建一个Struts2项目maven 方式
  • 从tcpdump抓包看TCP/IP协议
  • 大型网站性能监测、分析与优化常见问题QA
  • 搞机器学习要哪些技能
  • 关于字符编码你应该知道的事情
  • 好的网址,关于.net 4.0 ,vs 2010
  • 解决jsp引用其他项目时出现的 cannot be resolved to a type错误
  • 面试题:给你个id,去拿到name,多叉树遍历
  • 如何利用MongoDB打造TOP榜小程序
  • 数据仓库的几种建模方法
  • 微信公众号开发小记——5.python微信红包
  • mysql 慢查询分析工具:pt-query-digest 在mac 上的安装使用 ...
  • ​一些不规范的GTID使用场景
  • ###C语言程序设计-----C语言学习(6)#
  • #考研#计算机文化知识1(局域网及网络互联)
  • #微信小程序(布局、渲染层基础知识)
  • $.type 怎么精确判断对象类型的 --(源码学习2)
  • (01)ORB-SLAM2源码无死角解析-(56) 闭环线程→计算Sim3:理论推导(1)求解s,t
  • (1)常见O(n^2)排序算法解析
  • (20)目标检测算法之YOLOv5计算预选框、详解anchor计算
  • (ZT)一个美国文科博士的YardLife
  • (附源码)ssm高校实验室 毕业设计 800008
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • (离散数学)逻辑连接词