Как поймать 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 обновил ответ Дирка, но Дирк отклонил изменение. Вот новый ответ Петра:
Хотя приведенный выше фрагмент является правильным примером 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;
}
Стандарт 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);
}