在C中捕获Ctrl-C

时间:2010-11-18 16:20:10

标签: c

如何在C中捕获 Ctrl + C

9 个答案:

答案 0 :(得分:181)

使用信号处理程序。

以下是翻转bool中使用的main()的简单示例:

#include <signal.h>

static volatile int keepRunning = 1;

void intHandler(int dummy) {
    keepRunning = 0;
}

// ...

int main(void) {

   signal(SIGINT, intHandler);

   while (keepRunning) { 
      // ...

2017年6月编辑:可能会引起关注,尤其是那些对编辑此答案抱怨无法满足的人。看,我在七年前写了这个答案。是的,语言标准会发生变化如果你真的必须改善这个世界,请添加你的新答案,但保留原样。由于答案上有我的名字,我更喜欢它也包含我的话。谢谢。

答案 1 :(得分:42)

点击此处:

注意:显然,这是一个简单的示例,解释只是如何设置 Ctrl C 处理程序,但一如既往需要遵守规则,以免破坏别的东西。请阅读以下评论。

上面的示例代码:

#include  <stdio.h>
#include  <signal.h>
#include  <stdlib.h>

void     INThandler(int);

int  main(void)
{
     signal(SIGINT, INThandler);
     while (1)
          pause();
     return 0;
}

void  INThandler(int sig)
{
     char  c;

     signal(sig, SIG_IGN);
     printf("OUCH, did you hit Ctrl-C?\n"
            "Do you really want to quit? [y/n] ");
     c = getchar();
     if (c == 'y' || c == 'Y')
          exit(0);
     else
          signal(SIGINT, INThandler);
     getchar(); // Get new line character
}

答案 2 :(得分:28)

关于UN * X平台的附录。

根据GNU / Linux上的signal(2)手册页,signal的行为不像sigaction的行为那样可移植:

  

signal()的行为在UNIX版本中有所不同,并且也是如此   不同版本的Linux在历史上各不相同。避免它   使用:改为使用sigaction(2)。

在System V上,系统没有阻止信号的其他实例的传递,并且信号的传递会将处理程序重置为默认值。在BSD中,语义发生了变化。

Dirk Eddelbuettel先前回答的以下变体使用sigaction代替signal

#include <signal.h>
#include <stdlib.h>

static bool keepRunning = true;

void intHandler(int) {
    keepRunning = false;
}

int main(int argc, char *argv[]) {
    struct sigaction act;
    act.sa_handler = intHandler;
    sigaction(SIGINT, &act, NULL);

    while (keepRunning) {
        // main loop
    }
}

答案 3 :(得分:11)

或者您可以将终端设置为原始模式,如下所示:

struct termios term;

term.c_iflag |= IGNBRK;
term.c_iflag &= ~(INLCR | ICRNL | IXON | IXOFF);
term.c_lflag &= ~(ICANON | ECHO | ECHOK | ECHOE | ECHONL | ISIG | IEXTEN);
term.c_cc[VMIN] = 1;
term.c_cc[VTIME] = 0;
tcsetattr(fileno(stdin), TCSANOW, &term);

现在应该可以使用fgetc(stdin)读取 Ctrl + C 击键。请注意使用它,因为你不能 Ctrl + Z Ctrl + Q Ctrl < / kbd> + S 等,正常情况下也是如此。

答案 4 :(得分:10)

设置陷阱(您可以使用一个处理程序捕获多个信号):

signal (SIGQUIT, my_handler);
signal (SIGINT, my_handler);

随时处理信号,但要注意限制和陷阱:

void my_handler (int sig)
{
  /* Your code here. */
}

答案 5 :(得分:4)

关于现有答案,请注意信号处理取决于平台。例如,Win32处理的信号远少于POSIX操作系统; see here。虽然在Win32上的signals.h中声明了SIGINT,但请参阅文档中的说明,该说明解释说它不会达到您的预期效果。

答案 6 :(得分:3)

#include<stdio.h>
#include<signal.h>
#include<unistd.h>

void sig_handler(int signo)
{
  if (signo == SIGINT)
    printf("received SIGINT\n");
}

int main(void)
{
  if (signal(SIGINT, sig_handler) == SIG_ERR)
  printf("\ncan't catch SIGINT\n");
  // A long long wait so that we can easily issue a signal to this process
  while(1) 
    sleep(1);
  return 0;
}

函数sig_handler检查传递的参数的值是否等于SIGINT,然后执行printf。

答案 7 :(得分:2)

这只是在退出前打印。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void sigint_handler(int);

int  main(void)
{
    signal(SIGINT, sigint_handler);

     while (1){
         pause();   
     }         
    return 0;
}

 void sigint_handler(int sig)
{
    /*do something*/
    printf("killing process %d\n",getpid());
    exit(0);
}

答案 8 :(得分:2)

@Peter Varo更新了Dirk的答案,但Dirk拒绝了更改。这是彼得的新答案:

尽管以上摘录是正确的示例,但应尽可能使用更新的类型和更高版本所提供的保证。因此,对于正在寻求符合的实现的人来说,这是一种更安全,更现代的选择:

#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

static volatile sig_atomic_t keep_running = 1;

static void sig_handler(int _)
{
    (void)_;
    keep_running = 0;
}

int main(void)
{
    signal(SIGINT, sig_handler);

    while (keep_running)
        puts("Still running...");

    puts("Stopped by signal `SIGINT'");
    return EXIT_SUCCESS;
}
  

C11标准:7.14§2标头<signal.h>声明了一种类型... sig_atomic_t,它是对象的(可能是易挥发的)整数类型,即使存在异步中断,也可以作为原子实体进行访问。

此外:

  

C11标准:7.14.1.1§5如果信号不是由于调用abortraise函数而产生的,则如果信号处理程序指的是具有static或线程存储持续时间的任何对象,该对象不是非锁定原子对象,而是通过为声明为volatile sig_atomic_t的对象分配值来实现...