Linux 如何捕获 Control+D 信号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1516122/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-03 17:45:16  来源:igfitidea点击:

How to capture Control+D signal?

clinuxunixsignals

提问by

I want to capture the Ctrl+Dsignal in my program and write a signal handler for it. How can I do that? I am working on Cand using a Linuxsystem.

我想在我的程序中捕获Ctrl+D信号并为其编写一个信号处理程序。我怎样才能做到这一点?我正在使用C 语言并使用Linux系统。

回答by Etienne Dechamps

Ctrl+Dis not a signal, it's EOF (End-Of-File). It closes the stdin pipe. If read(STDIN) returns 0, it means stdin closed, which means Ctrl+Dwas hit (assuming there is a keyboard at the other end of the pipe).

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

回答by Piotr Czapla

As far as I know Ctrl+Dis translated by the system to end of standard input so your app won't get any signal.

据我所知Ctrl+D由系统转换为标准输入的结尾,因此您的应用程序不会收到任何信号。

I think that the only way to intercept Ctrl+Dis to work directly with the system api (like accessing tty)

我认为拦截Ctrl+的唯一方法D是直接使用系统api(如访问tty)

回答by Pascal Cuoq

As others have already said, to handle Control+D, handle "end of file"s.

正如其他人已经说过的,要处理Control+ D,请处理“文件结尾”。

Control+Dis a piece of communication between the user and the pseudo-file that you see as stdin. It does not mean specifically "end of file", but more generally "flush the input I typed so far". Flushing means that any read()call on stdin in your program returns with the length of the input typed since the last flush. If the line is nonempty, the input becomes available to your program although the user did not type "return" yet. If the line is empty, then read()returns with zero, and that is interpreted as "end of file".

Control+D是用户和您视为 stdin 的伪文件之间的一段通信。它并不特别意味着“文件结束”,而是更普遍的“刷新我到目前为止输入的输入”。read()刷新意味着程序中对 stdin 的任何调用都会返回自上次刷新以来键入的输入的长度。如果该行非空,则尽管用户还没有键入“return”,但您的程序可以使用该输入。如果该行为空,则read()返回零,这被解释为“文件结尾”。

So when using Control+Dto end a program, it only works at the beginning of a line, or if you do it twice (first time to flush, second time for read()to return zero).

因此,当使用Control+D结束程序时,它仅适用于一行的开头,或者如果您执行两次(第一次刷新,第二次read()返回零)。

Try it:

尝试一下:

$ cat
foo
   (type Control-D once)
foofoo (read has returned "foo")
   (type Control-D again)
$

回答by sambowry

A minimalistic example:

一个简约的例子:

#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]='
#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;
}
'; 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); }

The program change the VEOF char (from Ctrl-D) to Ctrl-C and the VINTR char (from Ctrl-C) to Ctrl-D. If You press Ctrl-D then the terminal driver will send a SIGINT to the signal handler of the program.

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

Note: pressing VINTR will erase the terminal input buffer so You can not read the characters typed in the line before the VINTR key pressed.

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

回答by user175104

You can use poll() and watch for POLLHUP on fd #1, because the TTY layer translates ^D to EOF.

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

回答by Doug

There's no need to process signals.

无需处理信号。

You need to ensure ISIG is not set on the terminal flags, that's all.

您需要确保未在终端标志上设置 ISIG,仅此而已。

Here's a complete contained example using select to avoid blocking on stdin:

这是一个完整的包含示例,使用 select 来避免在 stdin 上阻塞:

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

Output:

输出:

##代码##

NB. 3 is control C and 4 is control D; 26 is control z. 113 is 'q'. See: http://en.wikipedia.org/wiki/ASCII#ASCII_control_charactersfor a full table.

注意。3是控制C,4是控制D;26是对照z。113 是“q”。有关完整表格,请参阅:http: //en.wikipedia.org/wiki/ASCII#ASCII_control_characters