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。

阅读全文
0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 开庭后被告威胁我们证人怎么办 开车撞了豪车怎么办 我把人撞了全责怎么办 开车撞伤人没钱赔怎么办 开车撞伤无证驾驶人怎么办 开车把人撞伤了只买交强险怎么办 开车撞伤人赔不起怎么办 如果车撞死人了怎么办 给车撞了跑了怎么办 开车把人撞死了怎么办 开车把人蹭了怎么办 驾照被扣54分怎么办? 驾照被扣了12分怎么办 驾驶证丢失后被扣分怎么办 驾照扣了三十分怎么办 一次被扣了12分怎么办 驾照被扣35分后怎么办 我驾驶证扣了6分怎么办 c1驾照被扣6分怎么办 被扣了6分怎么办 今年扣了6分怎么办 驾照分不够扣了怎么办 被扣6分怎么办罚款15 驾照分数扣36分怎么办 驾照12分不够扣怎么办 驾照b证扣分了怎么办 违章18分不够扣怎么办 b2驾驶证扣11分怎么办 驾照一共扣30分怎么办 驾照扣了100分怎么办 驾照扣40多分怎么办 驾照被扣69分怎么办 驾照被扣200多分怎么办 b2驾照扣了分怎么办 驾驶证在外地被冒用怎么办 身份证被冒用办驾照怎么办 外国人在中国境内死了怎么办 台风来临时航船应该怎么办 越南攻占太平岛台湾怎么办 厨房下水pvc管三通漏水怎么办 pvc下水管道接口漏水怎么办