在C中捕获Ctrl-C

在C中捕获Ctrl-C,c,signals,c89,c99,c11,C,Signals,C89,C99,C11,如何在C中捕捉Ctrl+C?和信号处理程序 下面是一个简单的例子,翻转main()中使用的bool: #包括 静态volatile int keepRunning=1; void intHandler(int-dummy){ 保持修剪=0; } // ... 内部主(空){ 信号(SIGINT,intHandler); 当(继续修剪){ // ... 2017年6月编辑:可能会涉及到谁,特别是那些对编辑这个答案有着永不满足的渴望的人。看,我七年前写了这个答案。是的,语言标准改变了。如果你真的想

如何在C中捕捉Ctrl+C?

和信号处理程序

下面是一个简单的例子,翻转
main()
中使用的
bool

#包括
静态volatile int keepRunning=1;
void intHandler(int-dummy){
保持修剪=0;
}
// ...
内部主(空){
信号(SIGINT,intHandler);
当(继续修剪){
// ...
2017年6月编辑:可能会涉及到谁,特别是那些对编辑这个答案有着永不满足的渴望的人。看,我七年前写了这个答案。是的,语言标准改变了。如果你真的想让世界变得更好,请添加你的新答案,但保留我的答案。因为答案上有我的名字,我更希望它也包含我的话。而不是谢谢。

检查这里:

注意:显然,这是一个简单的示例,解释了如何设置CtrlC处理程序,但总是有一些规则需要遵守,以免破坏其他规则。请阅读下面的评论

上面的示例代码:

#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
}
#包括
#包括
#包括
void INThandler(int);
内部主(空)
{
信号(SIGINT,INThandler);
而(1)
暂停();
返回0;
}
无效INThandler(int sig)
{
字符c;
信号(信号,信号);
printf(“哎哟,你按Ctrl-C了吗?\n”
“您真的想退出吗?[y/n]”;
c=getchar();
如果(c=='y'| | c=='y')
出口(0);
其他的
信号(SIGINT,INThandler);
getchar();//获取新行字符
}
设置陷阱(您可以使用一个处理程序捕捉多个信号):

信号(SIGQUIT,我的_处理程序); 信号(SIGINT,我的处理器); 以您想要的方式处理信号,但要注意限制和问题:

void my_handler (int sig) { /* Your code here. */ } 作废我的_处理程序(int sig) { /*你的代码在这里*/ }
也可以将终端置于原始模式,如下所示:

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+S等。

关于现有答案,请注意信号处理依赖于平台。例如,Win32处理的信号比POSIX操作系统少得多。虽然Win32上的signals.h中声明了SIGINT,请参阅文档中的说明这说明它不会执行您可能期望的操作。

这只是在退出前打印

#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);
}
#包括
#包括
#包括
无效信号处理器(int);
内部主(空)
{
信号(SIGINT,SIGINT_处理器);
而(1){
暂停();
}         
返回0;
}
无效sigint_处理程序(int sig)
{
/*做点什么*/
printf(“正在终止进程%d\n”,getpid());
出口(0);
}

关于UN*X平台的附录

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

signal()的行为因UNIX版本而异,并且 不同版本的Linux历史上有所不同。请避免 使用:改为使用sigaction(2)

在SystemV上,系统没有阻止信号的进一步实例的传递,而信号的传递会将处理程序重置为默认处理程序。在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
    }
}
#包括
#包括
静态布尔修剪=真;
无效intHandler(int){
保持修剪=错误;
}
int main(int argc,char*argv[]){
结构动作法;
act.sa_handler=intHandler;
sigation(SIGINT,&act,NULL);
同时(继续修剪){
//主回路
}
}
#包括
#包括
#包括
无效信号处理器(int signo)
{
if(signo==SIGINT)
printf(“收到的信号”);
}
内部主(空)
{
if(信号(SIGINT,sig_处理程序)=sig_ERR)
printf(“\n无法捕获SIGINT\n”);
//长时间的等待,以便我们可以轻松地向该过程发出信号
而(1)
睡眠(1);
返回0;
}
函数sig_handler检查传递的参数值是否等于SIGINT,然后执行printf。

更新了Dirk的答案,但Dirk拒绝了更改。以下是Peter的新答案:

尽管上面的代码片段是一个正确的示例,但如果可能的话,应该使用更现代的类型和后期标准提供的保证。因此,对于寻求一致性实现的人来说,这里是一个更安全、更现代的替代方案:

#包括
#包括
#包括
静态易失性信号原子保持运行=1;
静态无效信号处理器(int)
{
(无效)—;
保持_运行=0;
}
内部主(空)
{
信号(SIGINT、sig_处理器);
while(保持运行)
放置(“仍在运行…”);
puts(“通过信号'SIGINT'停止”);
返回退出成功;
}
C11标准:7.14§2标题
声明一种类型…
sig_atomic_t
,它是对象的(可能是易失性限定的)整数类型,即使在存在异步中断的情况下也可以作为原子实体访问

此外:

C11标准:7.14.1.1§5如果信号不是由于调用
中止
提升
函数而产生的,则如果信号处理程序引用任何具有
静态
或线程存储持续时间且不是无锁原子对象的对象,而不是通过赋值,则该行为是未定义的声明为
易失性信号原子\u t
的对象


@Derrick同意,
int main
是一个合适的东西,但是
gcc
和其他编译器从1990年代开始将其编译成正确运行的程序。这里解释得很好:-这基本上是一个“特性”,我这样认为。@icyrock.com:所有都非常正确(关于C中的void main()),但在公开发布时,最好使用
#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);
}
#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
    }
}
#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;
}