代码之家  ›  专栏  ›  技术社区  ›  ryan

如何捕获控制+D信号?

  •  49
  • ryan  · 技术社区  · 17 年前

    我想抓住 Ctrl键 + D 在我的程序中输入信号,并为其编写一个信号处理程序。 我该怎么做? 我正在工作 C 使用 Linux 系统

    6 回复  |  直到 9 年前
        1
  •  90
  •   Fabrizio RAHUL S R    9 年前

    正如其他人已经说过的那样 控制 + D ,处理“文件结尾”。

    控制 + D 是用户和伪文件之间的一段通信,您将其视为stdin。它并不是特别指“文件结束”,而是更一般地说“刷新我目前键入的输入”。法拉盛意味着 read() 在程序中调用stdin返回自上次刷新以来输入的长度。如果该行为非空,则该输入可用于您的程序,尽管用户尚未键入“return”。如果线路是空的,那么 读() 返回零,这被解释为“文件结束”。

    所以当使用 控制 + D 要结束一个程序,它只在一行的开头工作,或者如果你做了两次(第一次刷新,第二次刷新) 读() 返回零)。

    试试看:

    $ cat
    foo
       (type Control-D once)
    foofoo (read has returned "foo")
       (type Control-D again)
    $
    
        2
  •  37
  •   Fabrizio RAHUL S R    9 年前

    Ctrl键 + 不是信号,而是EOF(文件结束)。它关闭了标准管道。如果read(STDIN)返回0,则表示STDIN已关闭,这意味着 Ctrl键 + D 被击中(假设管道的另一端有一个键盘)。

        3
  •  16
  •   sambowry    17 年前

    一个极简主义的例子:

    #include <unistd.h> 
    #include <stdio.h> 
    #include <termios.h> 
    #include <signal.h> 
    
    void sig_hnd(int sig){ (void)sig; printf("(VINTR)"); }
    
    int main(){
      setvbuf(stdout,NULL,_IONBF,0);
    
      struct termios old_termios, new_termios;
      tcgetattr(0,&old_termios);
    
      signal( SIGINT, sig_hnd );
    
      new_termios             = old_termios;
      new_termios.c_cc[VEOF]  = 3; // ^C
      new_termios.c_cc[VINTR] = 4; // ^D
      tcsetattr(0,TCSANOW,&new_termios);
    
      char line[256]; int len;
      do{
        len=read(0,line,256); line[len]='\0';
        if( len <0 ) printf("(len: %i)",len);
        if( len==0 ) printf("(VEOF)");
        if( len >0 ){
          if( line[len-1] == 10 ) printf("(line:'%.*s')\n",len-1,line);
          if( line[len-1] != 10 ) printf("(partial line:'%s')",line);
        }
      }while( line[0] != 'q' );
    
      tcsetattr(0,TCSANOW,&old_termios);
    }
    

    程序将VEOF char(从Ctrl-D)更改为Ctrl-C,将VINTR char(从Ctrl-C)更改为Ctrl-D。如果按Ctrl-D,终端驱动程序将向程序的信号处理程序发送SIGINT。

    注意:按下VINTR键将清除终端输入缓冲区,因此在按下VINTR键之前,您无法读取在该行中键入的字符。

        4
  •  5
  •   Paul Anton Umolac    11 年前

    你需要确保ISIG没有设置在终端标志上,仅此而已。

    下面是一个完整的示例,使用select可以避免在stdin上阻塞:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <termios.h>
    #include <time.h>
    #include <sys/select.h>
    
    #define STDIN_FILENO 0
    
    struct termios org_opts;
    
    /** Select to check if stdin has pending input */
    int pending_input(void) {
      struct timeval tv;
      fd_set fds;
      tv.tv_sec = 0;
      tv.tv_usec = 0;
      FD_ZERO(&fds);
      FD_SET(STDIN_FILENO, &fds); //STDIN_FILENO is 0
      select(STDIN_FILENO+1, &fds, NULL, NULL, &tv);
      return FD_ISSET(STDIN_FILENO, &fds);
    }
    
    /** Input terminal mode; save old, setup new */
    void setup_terminal(void) {
      struct termios new_opts;
      tcgetattr(STDIN_FILENO, &org_opts);
      memcpy(&new_opts, &org_opts, sizeof(new_opts));
      new_opts.c_lflag &= ~(ICANON | ECHO | ECHOE | ECHOK | ECHONL | ECHOPRT | ECHOKE | ISIG | ICRNL);
      tcsetattr(STDIN_FILENO, TCSANOW, &new_opts);
    }
    
    /** Shutdown terminal mode */
    void reset_terminal(void) {
      tcsetattr(STDIN_FILENO, TCSANOW, &org_opts);
    }
    
    /** Return next input or -1 if none */
    int next_input(void) {
      if (!pending_input())
        return -1;
      int rtn = fgetc(stdin);
      printf("Found: %d\n", rtn);
      return(rtn);
    }
    
    int main()
    {
      setup_terminal();
    
      printf("Press Q to quit...\n");
      for (;;) {
        int key = next_input();
        if (key != -1) {
          if ((key == 113) || (key == 81)) {
            printf("\nNormal exit\n");
            break;
          }
        }
      }
    
      reset_terminal();
      return 0;
    }
    

    输出:

    doug-2:rust-sys-sterm doug$ cc junk.c
    doug-2:rust-sys-sterm doug$ ./a.out
    Press Q to quit...
    Found: 4
    Found: 3
    Found: 27
    Found: 26
    Found: 113
    
    Normal exit
    

    注意。3为对照C,4为对照D;26是控制z。113是“q”。 见: http://en.wikipedia.org/wiki/ASCII#ASCII_control_characters 为了一张满桌的桌子。

        5
  •  4
  •   Fabrizio RAHUL S R    9 年前

    据我所知 Ctrl键 + D 被系统转换为标准输入的末尾,这样你的应用程序就不会收到任何信号。

    我认为只有这样才能拦截 Ctrl键 + D 直接使用系统api(比如访问tty)

        6
  •  1
  •   Georg Gbess    5 年前

    Ctrl键 + D ascci表中的值为4,是不可打印的字符。
    因此,您可以使用以下代码在终端中捕获它。 当getline函数 Ctrl键 + D 出现错误,返回值为-1。 您可以对返回值设置条件。

    #include <stdio.h>
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(void)
    {
        char *buf = malloc(sizeof(char) * 500);
        size_t size = 500;
        int nb = getline(&buf, &size, stdin);
        if (nb == -1)
            printf("CTRL + D captured\n");
        free(buf);
        return (0);
    }
    
        7
  •  0
  •   user175104    17 年前

    您可以使用poll()并在fd#1上查看POLLHUP,因为TTY层将^D转换为EOF。