开发者

Catch Ctrl-C in C

How does开发者_Go百科 one catch Ctrl+C in C?


With a signal handler.

Here is a simple example flipping a bool used in main():

#include <signal.h>

static volatile int keepRunning = 1;

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

// ...

int main(void) {

   signal(SIGINT, intHandler);

   while (keepRunning) { 
      // ...

Edit in June 2017: To whom it may concern, particularly those with an insatiable urge to edit this answer. Look, I wrote this answer seven years ago. Yes, language standards change. If you really must better the world, please add your new answer but leave mine as is. As the answer has my name on it, I'd prefer it to contain my words too. Thank you.


Check here:

  • http://www.csl.mtu.edu/cs4411.ck/www/NOTES/signal/install.html

Note: Obviously, this is a simple example explaining just how to set up a CtrlC handler, but as always there are rules that need to be obeyed in order not to break something else. Please read the comments below.

The sample code from above:

#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
}


Addendum regarding UN*X platforms.

According to the signal(2) man page on GNU/Linux, the behavior of signal is not as portable as behavior of sigaction:

The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.

On System V, system did not block delivery of further instances of the signal and delivery of a signal would reset the handler to the default one. In BSD the semantics changed.

The following variation of previous answer by Dirk Eddelbuettel uses sigaction instead of 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
    }
}


@Peter Varo updated Dirk's answer, but Dirk rejected the change. Here's the new answer by Peter:

Although the above snippet is a correct c89 example, one should use the more modern types and guarantees provided by the later standards if possible. Therefore, here is a safer and modern alternative for those who are seeking for the c99 and c11 conforming implementation:

#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 Standard: 7.14§2 The header <signal.h> declare a type ... sig_atomic_t which is the (possibly volatile-qualified) integer type of an object that can be accessed as an atomic entity, even in the presence of asynchronous interrupts.

Furthermore:

C11 Standard: 7.14.1.1§5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t...


Or you can put the terminal in raw mode, like this:

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);

Now it should be possible to read Ctrl+C keystrokes using fgetc(stdin). Beware using this though because you can't Ctrl+Z, Ctrl+Q, Ctrl+S, etc. like normally any more either.


Set up a trap (you can trap several signals with one handler):

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

Handle the signal however you want, but be aware of limitations and gotchas:

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


Regarding existing answers, note that signal handling is platform dependent. Win32 for example handles far fewer signals than POSIX operating systems; see here. While SIGINT is declared in signals.h on Win32, see the note in the documentation that explains that it will not do what you might expect.


#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;
}

The function sig_handler checks if the value of the argument passed is equal to the SIGINT, then the printf is executed.


This just print before exit.

#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);
}
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜