如何捕获Control + D信号?
我想在我的程序中捕获Ctrl + D信号,并为它写一个信号处理程序。 我怎样才能做到这一点? 我正在C和使用Linux系统。
正如其他人已经说过,处理Control + D ,处理“文件的结尾”。
Control + D是用户和作为标准input显示的伪文件之间的一个通信。 这并不是特指“文件结束”,而是更一般地说“冲洗我迄今input的input”。 刷新意味着程序中stdin上的任何read()
调用将返回自上次刷新后input的input的长度。 如果该行不为空,则input可用于您的程序,但用户尚未键入“返回”。 如果该行为空,则read()
返回零,并将其解释为“文件结尾”。
所以当使用Control + D来结束一个程序时,它只能在一行的开始,或者如果你做了两次(第一次刷新,第二次read()
返回零)。
尝试一下:
$ cat foo (type Control-D once) foofoo (read has returned "foo") (type Control-D again) $
Ctrl + D不是一个信号,它是EOF(文件结束)。 它closuresstdinpipe道。 如果read(STDIN)返回0,这意味着stdin被closures,这意味着Ctrl + D被击中(假设在pipe道的另一端有一个键盘)。
一个简约的例子:
#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字符(从Ctrl-D)更改为Ctrl-C,将VINTR字符(从Ctrl-C)更改为Ctrl-D。 如果您按下Ctrl-D,则terminal驱动程序将向程序的信号处理程序发送SIGINT。
注意:按VINTR将清除terminalinput缓冲区,因此在按下VINTR键之前,您无法读取在行中input的字符。
据我所知, Ctrl + D被系统翻译为标准input结束,所以你的应用程序不会得到任何信号。
我认为拦截Ctrl + D的唯一方法是直接使用系统API(如访问tty)
没有必要处理信号。
你需要确保在terminal标志上没有设置ISIG,就这些了。
这是一个完整的包含示例使用select,以避免在标准input阻塞:
#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
NB。 3是控制C,4是控制D; 26是控制z。 113是'q'。 查看: http : //en.wikipedia.org/wiki/ASCII#ASCII_control_characters查看完整表格。
您可以使用poll()并观察fd#1上的POLLHUP,因为TTY层将^ D转换为EOF。
信号只能被抓住,所以你可以抓住^ C而不是^ D
因为这不是一个信号…
希望这可以帮助