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

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

9 ответов

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

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

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

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

Приложение о платформах UN*X.

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

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

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

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

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

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

Стандарт С11: 7.14§2 Заголовок <signal.h> объявить тип... sig_atomic_t это целочисленный тип (возможно, изменчивый) объекта, к которому можно обращаться как к элементарному объекту, даже при наличии асинхронных прерываний.

Более того:

Стандарт C11: 7.14.1.1§5 Если сигнал возникает не в результате вызова abort или же raise функция, поведение не определено, если обработчик сигнала ссылается на любой объект с static или продолжительность хранения потока, которая не является атомарным объектом без блокировки, кроме как путем присвоения значения объекту, объявленному как volatile sig_atomic_t...

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

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 и т. Д., Как обычно.

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

сигнал (SIGQUIT, my_handler);
сигнал (SIGINT, my_handler);

Обрабатывайте сигнал как хотите, но помните об ограничениях и ошибках:

void my_handler (int sig)
{
  / * Ваш код здесь. */
}

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

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

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

#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);
}
Другие вопросы по тегам