Linux系统编程学习笔记(4)-对终端进行控制
来源:互联网 发布:net snmp windows编程 编辑:程序博客网 时间:2024/06/08 12:20
Linux中的设备
Linux系统中,设备就像文件,也可以对设备进行读写操作,与文件类似,每个设备也都有一个文件名,一个i-节点,一个文件所有者,一个权限为和最后修改时间。特别的是,终端在Linux中也体现为文件,可以通过命令:
$ tty/dev/pts/1
查看终端号,在其他终端(或是程序)对这个终端进行些操作,可以在此终端读取到写的内容.
与不同文件或文件夹不同的是,终端设备的权限为首字母为“c”(目录为d), c表示终端文件其实是以字符为单位进行传送的设备。
另一个不同之处在于,输入如下命令:
$ ls -li /dev/pts/1
或是
$ ls -li /dev/pts/2
得到的内容中,文件的大小号一致,都为136。
对于设备来讲,文件大小位储存的并不是文件的字节数,而是指向内核子程序的指针, 内核内的这种传输设备数据的子程序被称为设备驱动程序。对于终端/dev/pts/2, 136称之为主设备好, 2称之为从设备号。主设备号确定处理该设备实际的子程序, 而从设备号被作为参数传输到该子程序。
一个简单的向终端写内容的程序如下:
#include <iostream>#include <stdio.h>#include <fcntl.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#define BUFSIZ 100using namespace std;int main(int ac, char *av[]){ int fd; char buf[BUFSIZ]; if(ac != 2){ fprintf(stderr, "usage: write0 ttyname\n"); exit(1); } fd = open(av[1], O_WRONLY); if(fd == -1){ perror(av[1]); exit(1); } while(fgets(buf, BUFSIZ, stdin) != NULL) if(write(fd, buf, strlen(buf)) == -1) break; close(fd);
其中函数
fgets()
用来从终端读取数据,stdin为标准输入。
连接的属性
调用系统函数open,创建文件描述符,会在进程与文件(设备)之间创建一个属性,系统根据这些属性来判断如何对文件进行读取或读取的形式如何。
磁盘连接的属性
磁盘主要的两个属性为缓冲与自动添加模式,下面将进行详细介绍。
属性1:缓冲
前面已经提到过,进程对磁盘文件的读写实际上述读取缓冲区的内容,修改此属性的步骤如下:
#include <fcntl.h>int s;s = fcntl(fd, F_GETEL);s |= O_SYNC;result = fcntl(fd, F_SETEL, s);if(result == 1) perror("setting SYNC");
系统函数调用方式如下:
#include <fcntl.h>#include <unistd.h>#include <sys/types.h>/********************************param fd 需控制的文件描述符param cmd 需进行的操作param arg 操作的参数param loak 锁信息return -1 遇到错误 other 依操作而定*********************************/int result = fcntl(int fd, int cmd);int result = fcntl(int fd, int cmd, long arg);int result = fcntl(int fd, int cmd, struct flock *lockp);
fcntl函数在指定的文件上执行操作cmd。arg代表操作cmd所使用的一个参数。上例中使用的操作位O_SYNC即告诉内核, 对write的调用仅能在数据写入实际的硬件时才能返回,而不是在数据复制到内核缓冲时就执行默认的返回操作。
属性2:自动添加模式
文件描述符的另一个属性是自动添加模式。自动添加模式对于若干个进程在同一时间写入文件很有用。设置自动添加模式的的文件描述符位为O_APPEND,此位被开启后,对于每个write的调用会自动调用lseek将内容添加到文件的末尾。启动自动添加模式的代码如下:
#include <fcntl.h>int s;s = fcntl(fd, F_GETEL);s |= O_APPEND;result = fcntl(fd, F_SETET, s);if(result == -1) perror("setting APPEND");else write(fd, &rec, 1);
另一种设置文件描述符属性的方法是使用open函数,具体可参照学习笔记第一部分:
http://blog.csdn.net/qq_34561506/article/details/76648581
终端连接属性
终端的属性较多,从波特率,是否回显信息,到键盘上对应的字符表示哪种指令。最简单的命令要数stty命令,可以直接在终端设置属性。例如:
$ stty erase x
就将x设置成了删除键。
程序想要该表终端驱动程序的设置要使用系统函数tcgetattr()与tcsetattr()来读取属性并设置属性。
#include <termios.h>#include <unistd.h>/********************************param fd 与终端相连的文件描述符param info 指向终端结构的指针result -1 遇到错误 0 成功返回*********************************/int result = tcgetattr(int fd, struct termios *info)
tcgetattr函数从fd中获取当前设置, 并把它复制到info指针所指的结构中。
#include <termios.h>#include <unistd.h>/********************************param fd 与终端相连的文件描述符param when 改变设置的时间param info 指向终端结构的指针result -1 遇到错误 0 成功返回*********************************/int result = tcsetattr(int fd, int when, struct termios *info)
when的允许值如下:
- TCSANOW 立即更新驱动程序设置
- TCSADRAIN 等待直到驱动程序队列中的所有输出都被传送到终端,然后在更新
- TCSAFLUSH 等待直到驱动程序队列中的所有输出都被传送出去,然后释放所有队列中的输入数据,并进行一定的变化。
结构体termios中的内容如下:
struct termios{ tcflag_t c_iflag tcflag_t c_oflag tcflag_t c_cflag 设置奇偶性 tcflag_t c_lflag 显示回显位,终端是否回显内容 cc_t c_cc[NCCS] 控制字符 speed_t c_ispeed 波特率 speed_t c_ospeed}
具体操作:
测试位: if(flagset & MASK)置位: flagset |= MASK清除位: flagset &= ~MASK
常用的形式如下:
#include <termios.h>struct termios attribs;tcgetattr(fd, &settigns);settings.c_lflag |= ECHO;tcsetattr(fd, TCSANOW, &settings);
代码示例
int get_ok_char(){ int c; while ((c = getchar()) != EOF&&strchr("yYnN", c) == NULL) ; return c;}int get_response(char *question, int maxtries){ int input; printf("%s (y/n)", question); fflush(stdout); while(1){ sleep(SLEEPTIME); input = tolower(get_ok_char()); if(input == 'y') return 0; if(input == 'n') return 1; if(maxtries-- == 0) return 2; BEEP; }}int set_cr_noecho_mode(){ //将标准输入设置为无回显 struct termios ttystate; tcgetattr(0, &ttystate); ttystate.c_lflag &= ~ICANON; //ttystate.c_lflag &= ~ECHO; ttystate.c_cc[VMIN] = 0; ttystate.c_cc[VTIME] = 20; tcsetattr(0, TCSANOW, &ttystate);}int set_nodelay_mode(){ int termflags; termflags = fcntl(0, F_GETFL); //获取文件描述词 //termflags |= O_NDELAY; fcntl(0, F_SETFL, termflags);}int tty_mode(int how){ static struct termios original_mode; static int original_flags; if(how == 0){ tcgetattr(0, &original_mode); //获取终端属性 original_flags = fcntl(0, F_GETFL); //读取文件描述词 } else{ tcsetattr(0, TCSANOW, &original_mode); //复原终端属性 fcntl(0, F_SETFL, original_flags); //设置文件描述词标志 }}int main(){ int response; tty_mode(0); set_cr_noecho_mode(); set_nodelay_mode(); response = get_response(ASK, TRIES); tty_mode(1); return response;}
上述代码的主要目的是测试改变终端属性之后的终端操作模式,其中
ttystate.c_lflag &= ~ICANON
目的是取消终端的输入缓冲模式,具体体现为在终端输入后,不需要按回车键程序就开始处理输入字符,同时与之匹配的是
ttystate.c_cc[VMIN] = 0;
终端设置为一次处理一个字符。
ttystate.c_lflag &= ~ECHO
会消除终端回显功能。
非阻塞输入
上述代码体现了两种非阻塞的输入模式,分别为调用系统函数fcntl与设置等待时间VTIME
系统函数fcntl
fcntl函数的功能是根据文件描述词来操作文件的特性,用法如下:
int fcntl(int fd, int cmd);int fcntl(int fd, int cmd, long arg);int fcntl(int fd, int cmd, struct flock *lock);param:fd:文件描述词cmd:操作命令arg:供命令使用的参数lock:锁定文件时使用的参数
此次使用的设置非阻塞操作格式如下:
int termflags;termflags = fcntl(0, F_GETFL); //获取文件状态标志termflags |= O_NDELAY; //设置终端为非阻塞fcntl(0, F_SETFL,termflags); //设置文件状态标志
设置延迟时间VTIME
在终端状态结构体中,VTIME数据为超时时间设置,单位为ms,但使用此方法是有讲究的,需要和VMIN同时使用,VMIN用于设置读取字符的最小数量。
1.当VTIME>0,VMIN>0时。read(或者其他读取调用)调用将保持阻塞直到读取到第一个字符,读到了第一个字符之后开始计时,此后若时间到了VTIME或者时间未到但已读够了VMIN个字符则会返回;若在时间未到之前又读到了一个字符(但此时读到的总数仍不够VMIN)则计时重新开始。
2. 当VTIME>0,VMIN=0时。read调用读到数据则立即返回,否则将为每个字符最多等待VTIME时间。
3. 当VTIME=0,VMIN>0时。read调用一直阻塞,直到读到VMIN个字符后立即返回。
4. 若在open或fcntl设置了O_NDELALY或O_NONBLOCK标志,read调用不会阻塞而是立即返回,那么VTIME和VMIN就没有意义,效果等同于与把VTIME和VMIN都设为了0。
- Linux系统编程学习笔记(4)-对终端进行控制
- 《unix/linux编程实践教程》学习笔记:第六章 终端控制与信号
- linux 对终端进行读写
- linux系统编程之进程(五):终端、作业控制与守护进程
- linux系统编程之进程(五):终端、作业控制与守护进程
- 学习笔记-Linux系统编程
- Linux系统编程学习之《进程控制》
- Linux系统学习笔记:异常控制流
- unix\linux编程学习笔记之终端模式
- “笨办法学python”学习笔记-在终端powershell中对目录进行简单的编辑(一)
- “笨办法学python”学习笔记-在终端powershell中对目录进行简单的编辑(二)
- 《UNIX环境高级编程》笔记--控制终端
- Linux系统编程(25)——终端
- Linux系统编程--进程控制(一)
- Linux系统编程学习笔记(七)内存管理
- Linux系统编程学习笔记(五)进程管理1
- Linux系统编程学习笔记(十一)守护进程
- linux学习笔记-学生信息管理系统(shell编程)
- restful api best practice
- 关于 python ImportError: No module named 的问题
- Frida官方手册
- 如何修改自动同步数据的默认开关
- Maven 聚合(modules标签)与继承(parent标签)的笔记
- Linux系统编程学习笔记(4)-对终端进行控制
- socket 并发服务器 多线程模式
- bootstrapvalidator.js的学习
- android虚拟机访问本地Tomcat
- iOS-详解KVO底层实现
- Kotlin-属性-接口-修饰符-数据类
- 对需要映射实体类的项目 进行 hibernate 方面的支持
- Ubuntu学习篇:如何提高Ubuntu系统下载软件的速度。
- socket函数一直返回-1的问题