禁用中断字符和更改文件结束符为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相关推荐

  1. unix环境高级编程 pdf_UNIX系统编程宝典,每一本都值得程序员珍藏

    这几本UNIX系统编程宝典,重印无数次,几代程序员都视如珍宝的几本书,小编在出版圈里快十年了,见证了这本书图灵版.异步社区版的出版.营销,对这套书倾注了一定的感情.今天继续分享给你们,好书总会有人还不 ...

  2. 《Unix环境高级编程》学习笔记:从点到面

    以前在课堂上学习过<Unix初级教程(第四版)>,对于Unix有了一点了解.由于以后使用的需要,要对它进行比较深入的学习,为此需要阅读不少的书籍,这本<Unix环境高级编程>便 ...

  3. 《UNIX环境高级编程(第3版)》——2.6 选项

    本节书摘来自异步社区<UNIX环境高级编程(第3版)>一书中的第2章,第2.6节,作者:[美]W. Richard Stevens , Stephen A.Rago著,更多章节内容可以访问 ...

  4. 【UNIX环境高级编程】

    [UNIX环境高级编程] 环境搭建 1.下载源码 wget http://apuebook.com/src.3e.tar.gz 2.解压 tar -zxvf src.3e.tar.gz 3.安装lib ...

  5. Unix环境高级编程 笔记

    Unix环境高级编程(第二版)学习笔记 这是一次较长时间的整理,然而跳跃了一些章节和很多知识点,仍然是很不完善很不全面的. 前言 操作系统某些问题 严格意义上,可将操作系统定义为一种软件,它控制计算机 ...

  6. 《UNIX环境高级编程(第3版)》

    <UNIX环境高级编程(第3版)> 基本信息 原书名:Advanced Programming in the UNIX Environment (3rd Edition) (Addison ...

  7. UNIX环境高级编程-第三版

    Unix环境高级编程-第三版 之前学习了<Linux系统编程>对于常见的概念和函数都有了基础的认知,这里准备通过这本书,深入学习系统API相关内容.笔记内容会有所倾向,不会严格反应书本内容 ...

  8. UNIX环境高级编程(屏幕打印和inet_ntoa输出异常问题)

    一.来回在屏幕和文件之间打印 在网上没找到在屏幕和文件来回打印的方法,翻了下UNIX环境高级编,里面有个freopen用于在一个指定的流上打开一个指定的文件,如果要将标准输出打印到文件,需要调用该函数 ...

  9. 《UNIX 环境高级编程》学习笔记—— 标准I/O库

    UNIX环境高级编程--标准I/O库 流和 FILE 对象 标准输入.标准输出和标准错误 缓冲 打开流 读和写流 每次一行 I/O 二进制 I/O 定位流 格式化 I/O 临时文件 内存流 流和 FI ...

  10. unix环境高级编程(上)-文件篇

    目录 前言 unix基础知识 unix标准化和实现 unix提供的文件IO 文件和目录 标准IO 系统数据文件 前言 笔者将<unix环境高级编程>主要内容总结为三篇:文件篇,进程篇,高级 ...

最新文章

  1. 想转行?零基础该如何学Python?这些一定要明白
  2. 老式Android中碎片Fragment之间的跳转和数据传递
  3. php七牛云储存图片,wordpress使用七牛云存储图片 | 厘米天空
  4. sharePoint查看与更改用户登录账号
  5. 【CV论文阅读】Rank Pooling for Action Recognition
  6. Java 结构体之 JavaStruct 使用教程二 JavaStruct 用例分析
  7. UE4中的字符串转换
  8. 前端VUE工程不占用80端口,浏览器不带端口访问VUE项目的实现
  9. 暂停停止继续播放 0201 winform
  10. 怎样彻底删除系统服务项
  11. 【转载】6种.net分布式缓存解决方案
  12. linux如何检查进程,如何在Linux中检查进程的执行时间
  13. Node-介绍与模块化
  14. HP DL360 G7通过iLO部署系统
  15. dsp c语言程序设计,DSP C语言程序设计.pdf
  16. powerShell赋权限
  17. hex2bin() 函数
  18. mysql触发器安全吗_猎八哥浅谈MYSQL触发器
  19. 使用批处理解决U盘内出现的同名文件夹EXE病毒问题
  20. 【华为MateBook13】更换1TB固态硬盘SSD+重装win10系统+安装NVIDIA显卡驱动+电脑管家+指纹驱动+蓝牙驱动+Office激活

热门文章

  1. Photoshop 2021 22.4.3 精简版
  2. IDEA 如何 buil dpath
  3. Python AI 换脸,宋小宝都能换脸刘亦菲,你的网恋对象不知道有多可怕!
  4. 明日方舟公式计算机,【科普向】明日方舟里的伤害计算公式
  5. Jeaf Dean万字长文回顾2020谷歌技术发展(上)
  6. 武汉轻工大学计算机学院宿舍,武汉轻工大学有几个校区及校区地址 哪个校区最好...
  7. 阿里云商标顾问注册申请流程及常见问题解答
  8. 二阶低通滤波器IIR的五个参数推导过程
  9. 团队作业10——事后诸葛亮分析
  10. 基于Problem Solving with Algorithms and Data Structures using Python的学习记录(4)——Recursion