Поймать Ctrl-C в C

Как поймать Ctrl + C в C?

Ответ 1

С обработчиком сигнала.

Вот простой пример переключения 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 года. Кому это может касаться, особенно тем, у кого ненасытное желание отредактировать этот ответ. Смотри, я написал этот ответ семь лет назад. Да, языковые стандарты меняются. Если вам действительно нужно улучшить мир, пожалуйста, добавьте свой новый ответ, но оставьте мой как есть. Поскольку в ответе указано мое имя, я бы предпочел, чтобы в нем также содержались мои слова. Спасибо.

Ответ 2

Проверьте здесь:

Примечание.. Очевидно, это простой пример, объясняющий только, как настроить обработчик 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
}

Ответ 3

Добавление к платформам UN * X.

Согласно справочной странице signal(2) в GNU/Linux, поведение signal не так переносимо, как поведение sigaction:

Поведение сигнала() зависит от версий UNIX, а также разнообразно исторически в разных версиях Linux. Избегайте use: вместо этого используйте sigaction (2).

В системе V система не блокировала доставку дополнительных экземпляров сигнала, и доставка сигнала будет reset обработчиком по умолчанию. В BSD семантика изменилась.

Следующая вариация предыдущего ответа Дирка Эддельбуэттеля использует 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
    }
}

Ответ 4

Или вы можете поместить терминал в необработанный режим, например:

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

Теперь можно читать клавиши Ctrl + C с помощью fgetc(stdin). Остерегайтесь использовать это, потому что вы не можете Ctrl + Z, Ctrl + Q, Ctrl + S и т.д., Как обычно, либо больше.

Ответ 5

Настройте ловушку (вы можете захватить несколько сигналов одним обработчиком):

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

Обращайтесь с сигналом, как хотите, но будьте осторожны с ограничениями и gotchas:

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

Ответ 6

Что касается существующих ответов, обратите внимание, что обработка сигналов зависит от платформы. Win32, например, обрабатывает гораздо меньше сигналов, чем операционные системы POSIX; см. здесь. Хотя SIGINT объявлен в signal.h на Win32, см. Примечание в документации, в котором объясняется, что он не будет делать то, что вы ожидаете.

Ответ 7

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

Ответ 8

@Peter Varo обновил ответ Дирка, но Дирк отклонил изменение. Вот новый ответ Петра:

Хотя приведенный выше фрагмент является правильным примером , следует использовать более современные типы и гарантии, предоставляемые более поздними стандартами, если это возможно. Следовательно, вот более безопасная и современная альтернатива для тех, кто ищет реализацию, соответствующую и :

#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 Если сигнал возникает не в результате вызова функции abort или raise, поведение не определено, если обработчик сигнала ссылается на какой-либо объект со static или продолжительностью хранения потока, который не является свободным от блокировки атомарный объект, кроме присвоения значения объекту, объявленному как volatile sig_atomic_t...

Ответ 9

Это просто печать перед выходом.

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