zoukankan      html  css  js  c++  java
  • Understanding Unix/Linux Programming-用户程序:play_again3

      1 /* play_again3.c
      2  * purpuse: ask if user wants another play 
      3  * better : instant response without echo
      4               set tty into no-delay mode
      5               read char , return result
      6               reset terminal mode on Internet
      7  * returns: 0 -> yes , 1 -> no 
      8  */
      9  
     10  #include <stdio.h>
     11  #include <stdlib.h>
     12  #include <fcntl.h>
     13  #include <termios.h>
     14  #include <string.h>
     15  
     16  #define ASK "Do you want another play?"
     17  #define TRIES 3
     18  #define SLEEPTIME 2
     19  #define BEEP putchar('a');
     20 
     21 
     22  int get_response(char *);
     23  int get_ok_char(void);
     24  void set_nodelay_mode(void);
     25  void set_cr_noecho_mode(void);
     26  void tty_mode(int); 
     27  
     28  int main()
     29  {
     30      int response ;
     31      tty_mode(0);   // save tty mode
     32      set_cr_noecho_mode();
     33      set_nodelay_mode();
     34      response = get_response(ASK);
     35      tty_mode(1);   // restore tty mode
     36      return response ;
     37  }
     38  
     39  int get_response(char * qiz)
     40  {
     41      int input ;
     42      int maxtries = TRIES ;
     43      printf("%s(y/n)" , qiz);
     44      fflush(stdout);
     45      while(1)
     46      {
     47         BEEP ;
     48         sleep(SLEEPTIME);
     49         input = tolower(get_ok_char()) ;
     50         if(input == 'y')
     51         {
     52             printf("
    ");
     53             return 0 ;
     54         }
     55         if(input == 'n')
     56         {
     57             printf("
    ");
     58             return 1 ;
     59         }
     60         if(maxtries -- <= 0 )
     61         {
     62             printf("
    ");
     63             return 2 ;
     64         }
     65         BEEP ;
     66      }
     67  }
     68  
     69 int get_ok_char(void)
     70 {
     71     int c ;
     72     while( (c = getchar() ) != EOF && strchr("yYnN" , c ) == NULL )
     73         ;
     74     return c ;
     75 }
     76 
     77 void set_cr_noecho_mode(void)
     78 {
     79     struct  termios ttystate ;
     80     tcgetattr(0 , &ttystate);
     81     ttystate.c_lflag &= ~ICANON ;   // No Buffering
     82     ttystate.c_lflag &= ~ECHO ;
     83     ttystate.c_cc[VMIN] = 1 ;   //Get one char one time 
     84     tcsetattr( 0 , TCSANOW , &ttystate);    
     85 }
     86 
     87 void set_nodelay_mode(void)
     88 {
     89     int termflags ;
     90     termflags = fcntl(0 , F_GETFL);
     91     termflags |= O_NDELAY ;
     92     fcntl(0 , F_SETFL , termflags) ;
     93 }
     94 
     95 void tty_mode(int mode)
     96 {
     97     static struct termios original_mode ;// 设置静态结构体变量
     98     if(mode == 0 )
     99     {
    100         tcgetattr( 0 , & original_mode);// 存储原有设置
    101     }
    102     else
    103     {
    104         //还原原有设置
    105         if( tcsetattr(0 , TCSANOW , & original_mode) == -1 )
    106         {
    107             perror("Restore tty settings failed!
    ");
    108         }
    109     }
    110 }

     这里使用到了非阻塞输入

      怎么解释非阻塞输入与阻塞输入?

      书上解释:

        当调用getchar或者read从文件描述符读取输入时,这些调用通常会等待输入,这叫做阻塞输入(block input)。在play_again的例子中,对于getchar的调用使得程序一直等待用户的输入,知道用户输入一个字符。程序被阻塞,知道能够获得某些字符或是检测到文件的末尾。那么如何关闭输入阻塞呢?

        阻塞不仅仅是终端连接的属性,而是任何一个打开的文件的属性。(也就是说阻塞实际上是文件的属性咯,不论是磁盘文件还是设备文件)。毕竟程序或者进程是与文件通过文件描述符连接的。

        程序可以通过fcntl或者open为文件描述符启动非阻塞输入(nonblock input)。play_again3使用fcntl为文件描述符开启O_NDELAY标志。

        关闭一个文件描述符的阻塞状态并且调用read结果会如何呢?如果能够获得输入,read会获得输入并且返回所获得的字数。如果没有输入字符,read返回0,这就像遇到文件末尾一样,如果有错误,read返回1。

        非阻塞操作内部实现非常简单。每个文件都有一块保存未读取数据的地方。如果文件描述符置位O_NDELAY,并且那块空间是空的,则read调用返回0。

        阅读O_NDELAY相关的Linux源代码,就可以了解实现细节。(准备以后再阅读源码吧

    该程序的一些小问题:

    1.  运行在非阻塞模式,程序在调用getchar给用户输入字符之前睡眠2s,就算用户在1s内完成输入,程序也会在2s后得到字符
    2. 在显示提示符之后,对于fflush的调用。如果没有fflush,在调用getchar之前,提示符将不能显示。因为终端驱动程序不仅一行行地缓冲输入,而且还一行行地缓冲输出。驱动程序缓冲输出,直到它接收到一个换行符或者程序试图从终端读取输入。在这个例子中,为了给用户读提示符的时间,需要延迟读入,就必须调用fflush。
      1. 注意:fflush是将(输出)缓冲物理写入的函数。

    该程序的一些大问题:

      该程序忽略一切它不想要的字母,只识别合法输入,并在规定时间间隔内无合法输入的情况下自动退出。如果输入Ctrl-C将会如何?不但会中止该程序,也会中止终端程序。为何?因为该程序运行到字符输入时,终端处于非阻塞状态,shell调用获取命令行,但是因为处于非阻塞状态,read立即返回0,程序结束时处于一个错误的状态。

      而在其他一些情况中,像bash和tcsh这些shell,当程序退出或者死亡时,它们会重置终端的属性。

  • 相关阅读:
    jQuery Ajax学习
    jquery 学习
    jquery after append appendTo三个函数的区别
    rtmp服务器以及rtmp推流/拉流/转发
    Python字符编码详解
    Python自省(反射)指南
    Python线程指南
    Python正则表达式指南[转载]
    Python2.7 threading模块学习
    python中if __name__ == '__main__': 的解析
  • 原文地址:https://www.cnblogs.com/NJdonghao/p/5292319.html
Copyright © 2011-2022 走看看