《unix环境高级编程》--- 终端I/O
禁用中断字符和更改文件结束符为Ctrl+B
禁止使用终端驱动程序产生SIGINT信号的特殊字符。但仍可使用kill函数向进程发送该信号。
#include "apue.h"
#include <termios.h>int main(void)
{struct termios term;long vdisable;/* 仅当标准输入是终端设备时才修改终端特殊字符 */if(isatty(STDIN_FILENO) == 0)err_quit("standard input is not a terminal device");/* 获取_POSIX_VDISABLE值 */if((vdisable = fpathconf(STDIN_FILENO, _PC_VDISABLE)) < 0)err_quit("fpathconf error or _POSIX_VDISABLE not in effect");/* int tcgetattr(int filedes, struct termios *termptr);从内核获取termios结构。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 }; */if(tcgetattr(STDIN_FILENO, &term) < 0) /* fetch tty state */err_sys("tcgetattr error");term.c_cc[VINTR] = vdisable; /* disable INTR character */ term.c_cc[VEOF] = 2; /* EOF is Control-B*//* int tcsetattr(int filedes, int opt, const struct termios *termptr);TCSAFLUSH: 发送了所有输出后更改才发生。在更改发生时未读的所有输入数据都被删除(刷清)修改了termios结构后,设置属性 */if(tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0)err_sys("tcsetattr error");exit(0);
}
tcgetattr和tcsetattr实例
#include "apue.h"
#include <termios.h>int main(void)
{struct termios term;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("unkown bits/byte\n");}term.c_cflag &= ~CSIZE; /* zero out the bits */term.c_cflag |= CS8; /* set 8 bits/byte */if(tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0)err_sys("tcsetattr error");exit(0);
}
stty命令
ctermid函数
#include <stdio.h>char *ctermid(char *ptr);运行时确定控制终端的名字。因大多数UNIX系统都使用/dev/tty作为控制终端名,所以该函数主要为其他系统
提供可移植性。
返回值:成功则返回指向控制终端名的指针,出错返回指向空字符串的指针。
如果ptr非null,指向长度至少为L_ctermid字节的数组,进程的控制终端名存放在该数组。
L_ctermid定义在<stdio.h>中。若ptr是一个空指针,则为其分配空间(通常为静态变量),
然后进程的控制终端名存放在该数组中。
实现
#include <stdio.h>
#include <string.h>static char ctermid_name[L_ctermid];char *ctermid(char *str)
{if(str == NULL)str = ctermid_name;return (strcpy(str, "dev/tty")); /* strcpy() returns str */
}
isatty函数
#include <unistd.h>int isatty(int filedes);当描述符filedes引用一个终端设备时返回真。
实现
#include <termios.h>
#include "apue.h"int isatty(int fd)
{struct termios ts;return (tcgetattr(fd, &ts) != 1); /* true if no error (is a tty) */
}int main(void)
{printf("fd 0: %s\n", isatty(0) ? "tty" : " not a tty");printf("fd 1: %s\n", isatty(1) ? "tty" : " not a tty");printf("fd 2: %s\n", isatty(2) ? "tty" : " not a tty");exit(0);
}
ttyname函数
#include <unistd.h>char *ttyname(int fieldes);
返回在该文件描述符上打开的终端设备的路径名。
返回值:指向终端路径名的指针,出错返回NULL。
实现
读/dev目录,寻找具有相同设备号和i节点编号的表项。
终端名可能在/dev的子目录中,所以需搜索在/dev之下的整个文件子系统。会跳过很多产生不正确或
奇怪结果的目录,它们是/dev/.、/dev/..和/dev/fd。也跳过了一些别名,即/dev/stdin、/dev/stdout及
/dev/stderr,它们是对在/dev/fd目录文件的符号链接。
#include <sys/stat.h>
#include <dirent.h>
#include <limits.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
#include <stdlib.h>
#include "apue.h"struct devdir
{struct devdir *d_next;char *d_name;
};static struct devdir *head;
static struct devdir *tail;
static char pathname[_POSIX_PATH_MAX + 1];static void add(char *dirname)
{struct devdir *ddp;int len;len = strlen(dirname);/* Skip ., .., and /dev/fd. */if((dirname[len-1] == '.') && (dirname[len-2] == '/' ||(dirname[len-2] == '.' && dirname[len-3] == '/')))return;if(strcmp(dirname, "/dev/fd") == 0)return;ddp = malloc(sizeof(struct devdir));if(ddp == NULL)return;ddp->d_name = strdup(dirname);if(ddp->d_name == NULL){free(ddp);return;}ddp->d_next = NULL;if(tail == NULL){head = ddp;tail = ddp;}else{tail->d_next = ddp;tail = ddp;}
}static void cleanup(void)
{struct devdir *ddp, *nddp;ddp = head;while(ddp != NULL){nddp = ddp->d_next;free(ddp->d_name);free(ddp);ddp = nddp;}head = NULL;tail = NULL;
}static char *searchdir(char *dirname, struct stat *fdstatp)
{struct stat devstat;DIR *dp;int devlen;struct dirent *dirp;strcpy(pathname, dirname);if((dp = opendir(dirname)) == NULL)return (NULL);strcat(pathname, "/");devlen = strlen(pathname);while((dirp = readdir(dp)) != NULL){strncpy(pathname + devlen, dirp->d_name, _POSIX_PATH_MAX - devlen);/* Skip aliases */if(strcmp(pathname, "/dev/stdin") == 0 || strcmp(pathname, "/dev/stdout") == 0 ||strcmp(pathname, "/dev/stderr") == 0)continue;if(stat(pathname, &devstat) < 0)continue;if(S_ISDIR(devstat.st_mode)){add(pathname);continue;}/* found a match */if(devstat.st_ino == fdstatp->st_ino && devstat.st_dev == fdstatp->st_dev){closedir(dp);return (pathname);}}closedir(dp);return (NULL);
}char *ttyname(int fd)
{struct stat fdstat;struct devdir *ddp;char *rval;if(isatty(fd) == 0)return (NULL);if(fstat(fd, &fdstat) < 0)return (NULL);if(S_ISCHR(fdstat.st_mode) == 0)return (NULL);rval = searchdir("/dev", &fdstat);if(rval == NULL){for(ddp = head; ddp != NULL; ddp = ddp->d_next)if((rval = searchdir(ddp->d_name, &fdstat)) != NULL)break;}cleanup();return (rval);
}int main(void)
{char *name;if(isatty(0)){name = ttyname(0);if(name == NULL)name = "undefined";}elsename = "not a tty";printf("fd 0: %s\n", name);if(isatty(1)){name = ttyname(1);if(name == NULL)name = "undefined";}elsename = "not a tty";printf("fd 1: %s\n", name);if(isatty(2)){name = ttyname(2);if(name == NULL)name = "undefined";}elsename = "not a tty";printf("fd 2: %s\n", name);exit(0);}
getpass函数
读入用户在终端上键入的口令。由login(1)和crypt(1)调用。为了读口令,必须禁止回显,但仍可使终端以规范模式工作,因为用户键入口令后,一定要键入回车,从而构成完整行。
#include <signal.h>
#include <stdio.h>
#include <termios.h>
#include "apue.h"#define MAX_PASS_LEN 8 /* max #chars for user to enter */char *getpass(const char *prompt)
{static char buf[MAX_PASS_LEN + 1]; /* null byte at end */char *ptr;sigset_t sig, osig;struct termios ts, ots;FILE *fp;int c;/* 调用ctermid打开控制终端,而不是直接将/dev/tty写在程序中。以读、写模式打开控制终端 */if((fp = fopen(ctermid(NULL), "r+")) == NULL)return (NULL);/* 不带缓冲,否则需要fflush */setbuf(fp, NULL);/* 阻塞信号SIGINT和SIGTSTP,否则,在输入INTR或SUSP字符时会使程序终止,并使终端仍处于禁止回显状态。直到getpass返回前才解除对它们的阻塞。*/sigemptyset(&sig);sigaddset(&sig, SIGINT);sigaddset(&sig, SIGTSTP);sigprocmask(SIG_BLOCK, &sig, &osig);tcgetattr(fileno(fp), &ts);ots = ts;ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);tcsetattr(fileno(fp), TCSAFLUSH, &ts);fputs(prompt, fp);ptr = buf;while((c = getc(fp)) != EOF && c != '\n')if(ptr < &buf[MAX_PASS_LEN])*ptr++ = c;*ptr = 0;putc('\n', fp);tcsetattr(fileno(fp), TCSAFLUSH, &ots);sigprocmask(SIG_SETMASK, &osig, NULL);fclose(fp);return (buf);
}int main(void)
{char *ptr;if((ptr = getpass("Enter password:")) == NULL)err_sys("getpass error");printf("password: %s\n", ptr);/* getpass函数完成后,为安全起见,清除放过用户键入的未经加密的明文口令的存储区 */while(*ptr != 0)*ptr++ = 0;exit(0);
}
非规范模式 noncanonical mode
规范模式 canonical mode:
系统每次返回一行。
非规范模式 noncanonical mode:
在noncanonical模式,输入的数据不会被收集成行,并且特殊字符不会被处理:ERASE, KILL, EOF, NL, EOL, EOL2, CR, REPRINT, STATUS, WERASE。
当已经读取了指定数目的数据或者过了一个指定的时间之后,告诉系统返回。这个技术使用两个变量,它们存放在termios结构中的c_cc数组中:MIN和TIME。这两个数组的元素通过名称VMIN和VTIME被索引到。通过关闭termios结构中的c_lflag域中的ICANON标记来指定。
原始端模式和cbreak终端模式
#include "apue.h"
#include <termios.h>
#include <errno.h>static struct termios save_termios;
static int ttysavefd = -1;
static enum { RESET, RAW, CBREAK } ttystate = RESET;/*
cbreak模式:
1、非规范模式。不对某些特殊字符处理。
2、关闭回显标志。
3、每次输入一个一字节。为此将MIN设置为1,将TIME设置为0.
*/
int tty_cbreak(int fd)
{int err;struct termios buf;if(ttystate != RESET){errno = EINVAL;return (-1);}if(tcgetattr(fd, &buf) < 0)return (-1);save_termios = buf; /* structure copy *//* Echo off, canonical mode off */buf.c_lflag &= ~(ECHO | ICANON);/* 1 byte at a time, no timer */buf.c_cc[VMIN] = 1;buf.c_cc[VTIME] = 0;if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)return (-1);/* Verify that the changes stuck. tcsetattr can return 0 on partial success */if(tcgetattr(fd, &buf) < 0){err = errno;tcsetattr(fd, TCSAFLUSH, &save_termios);errno = err;return (-1);}if((buf.c_lflag & (ECHO | ICANON)) || buf.c_cc[VMIN] != 1 ||buf.c_cc[VTIME] != 0){tcsetattr(fd, TCSAFLUSH, &save_termios);errno = EINVAL;return (-1);}ttystate = CBREAK;ttysavefd = fd;return (0);
}/*
原始模式:
1、非规范模式。也关闭对产生信号字符(ISIG)和扩充输入字符(IEXTEN)的处理。另外,禁用BRKINT,使BREAK不再产生信号。
2、关闭回显(ECHO)标志。
3、禁用ICRNL、INPCK、ISTRIP和IXON标志。从而不再将输入的CR字符转换为NL(ICRNL),使奇偶校验不起作用(INPCK),不再剥离输入字节的第8位(ISTRIP),不再进行输出流控制(IXON)。
4、8位字符(CS8),且禁用奇偶性检测(PARENB)。
5、禁用所有输出处理(OPOST)
6、每次输入一个字节(MIN=1, TIME=0)
*/
int tty_raw(int fd)
{int err;struct termios buf;if(ttystate != RESET){errno = EINVAL;return (-1);}if(tcgetattr(fd, &buf) < 0)return (-1);save_termios = buf; /* structrue copy *//* Echo off, canonical mode off, extended input processing off,signal chars off */buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);/* No SIGINT on BREAK, CR-to-NL off, input parity check off,don't strip 8th bit on input, output flow control off */buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);/* Clear size bits, parity checking off */buf.c_cflag &= ~(CSIZE | PARENB);/* Set 8 bits/char */buf.c_cflag |= CS8;/* Output processing off */buf.c_oflag &= ~(OPOST);/* 1 byte at a time, no timer */buf.c_cc[VMIN] = 1;buf.c_cc[VTIME] = 0;if(tcsetattr(fd, TCSAFLUSH, &buf) < 0)return (-1);/* Verify that the changes stuck. tcsetarrt can return 0 on partial success */if(tcgetattr(fd, &buf) < 0){err = errno;tcsetattr(fd, TCSAFLUSH, &save_termios);errno = err;return (-1);}if((buf.c_lflag & (ECHO | ICANON | IEXTEN | ISIG)) || (buf.c_iflag & (BRKINT | ICRNL | INPCK | ISTRIP | IXON)) ||(buf.c_cflag & (CSIZE | PARENB | CS8)) != CS8 || (buf.c_oflag & OPOST) || buf.c_cc[VMIN] != 1 || buf.c_cc[VTIME] != 0){/* Only some of the change were made. Restore the original settings. */tcsetattr(fd, TCSAFLUSH, &save_termios);errno = EINVAL;return (-1);}ttystate = RAW;ttysavefd = fd;return (0);
}/* 将终端恢复位调用tty_cbreak和tty_raw之前的工作模式 */
int tty_reset(int fd)
{if(ttystate == RESET)return (0);if(tcsetattr(fd, TCSAFLUSH, &save_termios) < 0)return (-1);ttystate = RESET;return (0);
}/* 可被登记位终止处理程序, 以保证exit恢复终端工作模式*/
void tty_atexit(void)
{if(ttysavefd >= 0)tty_reset(ttysavefd);
}/* 返回一个指向原先的规范模式termios结构的指针 */
struct termios *tty_termios(void)
{return (&save_termios);
}static void sig_catch(int signo)
{printf("signal caught\n");tty_reset(STDIN_FILENO);exit(0);
}int main(void)
{int i;char c;/* 在编写更改终端模式的程序时,应捕捉大多数信号,以便在程序终止前恢复终端模式 */if(signal(SIGINT, sig_catch) == SIG_ERR)err_sys("signal(SIGINT) error");if(signal(SIGQUIT, sig_catch) == SIG_ERR)err_sys("signal(SIGQUIT) error");if(signal(SIGTERM, sig_catch) == SIG_ERR)err_sys("signal(SIGTERM) error");if(tty_raw(STDIN_FILENO) < 0)err_sys("tty_raw error");printf("Enter raw mode characters, terminate with 'a'\n");while((i = read(STDIN_FILENO, &c, 1)) == 1){if((c &= 255) == 97) /* 0177 = ASCII 'a' */break;printf("%o\n", c);}if(tty_reset(STDIN_FILENO) < 0)err_sys("tty_reset error");if(i <= 0)err_sys("read error");if(tty_cbreak(STDIN_FILENO) < 0)err_sys("tty_cbreak error");printf("\nEnter cbreak mode characters, terminate with SIGINT\n");while((i = read(STDIN_FILENO, &c, 1)) == 1){c &= 255;printf("%o\n", c);}if(tty_reset(STDIN_FILENO) < 0)err_sys("tty_reset error");if(i <= 0)err_sys("read error");exit(0);
}
结果
yjp@yjp-VirtualBox:~/apue/18termios$ ./tty
Enter raw mode characters, terminate with 'a'4 输入 Ctrl + D33 输入 F71336170176输入 a
Enter cbreak mode characters, terminate with SIGINT
1 输入 Ctrl + A
40 输入 空格
signal caught 输入 Ctrl + C
终端窗口大小
打印当前窗口大小,然后休眠。每次窗口大小改变时,就捕捉到SIGWINCH信号,然后打印新的窗口大小。
#include "apue.h"
#include <termios.h>
#ifndef TIOCGWINSZ
#include <sys/ioctl.h>
#endifstatic void pr_winsize(int fd)
{/*struct winsize{unsigned short ws_row; rows, in characters unsigned short ws_col; columns, in characters unsigned short ws_xpixel; horizontal size, pixels (unused)unsigned short ws_ypixel; vertical size, piexls (unused)}*/struct winsize size;/* 将winsize结构的新值存放在内核中。如果此新值与内核中的当前值不同,则向前台进程组发送SIGWINCH信号 */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(;;)pause(); /* and sleep forever */
}
结果
yjp@yjp-VirtualBox:~/apue/18termios$ ./winsize
24 rows, 80 columns 初始窗口大小
SIGWINCH received 更改窗口大小
25 rows, 84 columns
SIGWINCH received 更改窗口大小
25 rows, 63 columns
^C 输入Ctrl + C
《unix环境高级编程》--- 终端I/O相关推荐
- unix环境高级编程 pdf_UNIX系统编程宝典,每一本都值得程序员珍藏
这几本UNIX系统编程宝典,重印无数次,几代程序员都视如珍宝的几本书,小编在出版圈里快十年了,见证了这本书图灵版.异步社区版的出版.营销,对这套书倾注了一定的感情.今天继续分享给你们,好书总会有人还不 ...
- 《Unix环境高级编程》学习笔记:从点到面
以前在课堂上学习过<Unix初级教程(第四版)>,对于Unix有了一点了解.由于以后使用的需要,要对它进行比较深入的学习,为此需要阅读不少的书籍,这本<Unix环境高级编程>便 ...
- 《UNIX环境高级编程(第3版)》——2.6 选项
本节书摘来自异步社区<UNIX环境高级编程(第3版)>一书中的第2章,第2.6节,作者:[美]W. Richard Stevens , Stephen A.Rago著,更多章节内容可以访问 ...
- 【UNIX环境高级编程】
[UNIX环境高级编程] 环境搭建 1.下载源码 wget http://apuebook.com/src.3e.tar.gz 2.解压 tar -zxvf src.3e.tar.gz 3.安装lib ...
- Unix环境高级编程 笔记
Unix环境高级编程(第二版)学习笔记 这是一次较长时间的整理,然而跳跃了一些章节和很多知识点,仍然是很不完善很不全面的. 前言 操作系统某些问题 严格意义上,可将操作系统定义为一种软件,它控制计算机 ...
- 《UNIX环境高级编程(第3版)》
<UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...
- UNIX环境高级编程-第三版
Unix环境高级编程-第三版 之前学习了<Linux系统编程>对于常见的概念和函数都有了基础的认知,这里准备通过这本书,深入学习系统API相关内容.笔记内容会有所倾向,不会严格反应书本内容 ...
- UNIX环境高级编程(屏幕打印和inet_ntoa输出异常问题)
一.来回在屏幕和文件之间打印 在网上没找到在屏幕和文件来回打印的方法,翻了下UNIX环境高级编,里面有个freopen用于在一个指定的流上打开一个指定的文件,如果要将标准输出打印到文件,需要调用该函数 ...
- 《UNIX 环境高级编程》学习笔记—— 标准I/O库
UNIX环境高级编程--标准I/O库 流和 FILE 对象 标准输入.标准输出和标准错误 缓冲 打开流 读和写流 每次一行 I/O 二进制 I/O 定位流 格式化 I/O 临时文件 内存流 流和 FI ...
- unix环境高级编程(上)-文件篇
目录 前言 unix基础知识 unix标准化和实现 unix提供的文件IO 文件和目录 标准IO 系统数据文件 前言 笔者将<unix环境高级编程>主要内容总结为三篇:文件篇,进程篇,高级 ...
最新文章
- 想转行?零基础该如何学Python?这些一定要明白
- 老式Android中碎片Fragment之间的跳转和数据传递
- php七牛云储存图片,wordpress使用七牛云存储图片 | 厘米天空
- sharePoint查看与更改用户登录账号
- 【CV论文阅读】Rank Pooling for Action Recognition
- Java 结构体之 JavaStruct 使用教程二 JavaStruct 用例分析
- UE4中的字符串转换
- 前端VUE工程不占用80端口,浏览器不带端口访问VUE项目的实现
- 暂停停止继续播放 0201 winform
- 怎样彻底删除系统服务项
- 【转载】6种.net分布式缓存解决方案
- linux如何检查进程,如何在Linux中检查进程的执行时间
- Node-介绍与模块化
- HP DL360 G7通过iLO部署系统
- dsp c语言程序设计,DSP C语言程序设计.pdf
- powerShell赋权限
- hex2bin() 函数
- mysql触发器安全吗_猎八哥浅谈MYSQL触发器
- 使用批处理解决U盘内出现的同名文件夹EXE病毒问题
- 【华为MateBook13】更换1TB固态硬盘SSD+重装win10系统+安装NVIDIA显卡驱动+电脑管家+指纹驱动+蓝牙驱动+Office激活
热门文章
- Photoshop 2021 22.4.3 精简版
- IDEA 如何 buil dpath
- Python AI 换脸,宋小宝都能换脸刘亦菲,你的网恋对象不知道有多可怕!
- 明日方舟公式计算机,【科普向】明日方舟里的伤害计算公式
- Jeaf Dean万字长文回顾2020谷歌技术发展(上)
- 武汉轻工大学计算机学院宿舍,武汉轻工大学有几个校区及校区地址 哪个校区最好...
- 阿里云商标顾问注册申请流程及常见问题解答
- 二阶低通滤波器IIR的五个参数推导过程
- 团队作业10——事后诸葛亮分析
- 基于Problem Solving with Algorithms and Data Structures using Python的学习记录(4)——Recursion