为什么在按Ctrl + C时Linux总是输出“^ C”?

我一直在研究Linux的信号。 我已经完成了一个testing程序来捕获SIGINT。

#include <unistd.h> #include <signal.h> #include <iostream> void signal_handler(int signal_no); int main() { signal(SIGINT, signal_handler); for (int i = 0; i < 10; ++i) { std::cout << "I'm sleeping..." << std::endl; unsigned int one_ms = 1000; usleep(200* one_ms); } return 0; } void signal_handler(int signal_no) { if (signal_no == SIGINT) std::cout << "Oops, you pressed Ctrl+C!\n"; return; } 

虽然输出如下所示:

 I'm sleeping... I'm sleeping... ^COops, you pressed Ctrl+C! I'm sleeping... I'm sleeping... ^COops, you pressed Ctrl+C! I'm sleeping... ^COops, you pressed Ctrl+C! I'm sleeping... ^COops, you pressed Ctrl+C! I'm sleeping... ^COops, you pressed Ctrl+C! I'm sleeping... I'm sleeping... I'm sleeping... 

我知道按Ctrl + C时,前台进程组中的进程都会收到一个SIGINT(如果没有进程select忽略它)。

那么shell(bash)和上面程序的实例是否都收到了这个信号呢? 每个“哎呀”之前的“^ C”来自哪里?

操作系统是CentOS,而shell是bash。

Solutions Collecting From Web of "为什么在按Ctrl + C时Linux总是输出“^ C”?"

终端(驱动程序)拦截^ C并将其转换为发送给附加进程(即shell)的信号stty intr ^B将指示终端驱动程序拦截^ B。 它也是终端驱动程序,将^ C回显给终端。

shell只是一个位于行的另一端的进程,它通过终端驱动程序(如/ dev / ttyX)从终端接收stdin,stdout(和stderr)也会连接到同一个tty 。

请注意(如果启用了回显功能),终端将键击发送到进程(组)并返回到终端。 stty命令只是为了控制tty进程的tty驱动程序而包装ioctl()。

更新:为了证明shell不参与,我创建了下面的小程序。 它应该由它的父shell通过exec ./a.outexec ./a.outexec ./a.out ,它看起来是一个交互式shell将分支一个shell shell)程序将生成SIGINTR的密钥设置为^ B,切换echo off,并等待从stdin输入。

 #include <stdio.h> #include <string.h> #include <termios.h> #include <unistd.h> #include <signal.h> #include <errno.h> int thesignum = 0; void handler(int signum); void handler(int signum) { thesignum = signum;} #define THE_KEY 2 /* ^B */ int main(void) { int rc; struct termios mytermios; rc = tcgetattr(0 , &mytermios); printf("tcgetattr=%d\n", rc ); mytermios.c_cc[VINTR] = THE_KEY; /* set intr to ^B */ mytermios.c_lflag &= ~ECHO ; /* Dont echo */ rc = tcsetattr(0 , TCSANOW, &mytermios); printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc ); printf("Setting handler()\n" ); signal(SIGINT, handler); printf("entering pause()\n... type something followed by ^%c\n", '@'+THE_KEY ); rc = pause(); printf("Rc=%d: %d(%s), signum=%d\n", rc, errno , strerror(errno), thesignum ); // mytermios.c_cc[VINTR] = 3; /* reset intr to ^C */ mytermios.c_lflag |= ECHO ; /* Do echo */ rc = tcsetattr(0 , TCSANOW, &mytermios); printf("tcsetattr(intr,%d) =%d\n", THE_KEY, rc ); return 0; } 

intr.sh:

 #!/bin/sh echo $$ exec ./a.out echo I am back. 

shell响应你键入的所有内容,所以当你键入^C ,它也会被回显(在你的情况下,被你的信号处理程序拦截)。 命令stty -echo可能会对您有用,也可能不会对您有所帮助,具体取决于您的需求/限制,请参阅stty手册以获取更多信息。

当然,在任何时候通过外围设备驱动程序(例如用于生成^ C信号的键盘驱动程序,以及显示所有内容的终端驱动程序)与系统进行通信时,都会发生更多的情况。 您可以在程序集/机器语言,寄存器,查找表等的层面深入挖掘。如果您想深入了解以下书籍,可以从以下方面着手:

Unix操作系统的设计对于这些事情来说是一个很好的参考。 两个更经典的参考: UNIX环境下的Unix编程环境和高级编程

在这个SO问题的好总结在这里如何Ctrl-C终止子进程?

“当你运行一个程序的时候,比如find ,shell:

  • 壳叉本身
  • 并为孩子设置默认的信号处理
  • 用给定的命令替换孩子(例如用find)
  • 当你按下CTRL-C时,父shell会处理这个信号,但是孩子会收到它 – 默认的动作 – 终止。 (小孩也可以执行信号处理)“