Как можно поймать Ctrl+ Cв C?
Как можно поймать Ctrl+ Cв C?
Ответы:
С обработчиком сигнала.
Вот простой пример, переворачивающий 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 года : кому это может касаться, особенно тем, у кого есть ненасытное желание отредактировать этот ответ. Смотри, я написал этот ответ семь лет назад. Да, языковые стандарты меняются. Если вы действительно должны улучшить мир, пожалуйста, добавьте свой новый ответ, но оставьте мой как есть. Поскольку в ответе указано мое имя, я бы предпочел, чтобы в нем также содержались мои слова. Спасибо.
bool volatile keepRunning = true;
на 100% безопасно. Компилятор может свободно кэшировать данные keepRunning
в регистре, и volatile предотвращает это. На практике это, скорее всего, также может работать без ключевого слова volatile, когда цикл while вызывает хотя бы одну не встроенную функцию.
sig_atomic_t
либо atomic_bool
напечатал там. Я просто пропустил это. Теперь, поскольку мы говорим: хотите, чтобы я откатил мою последнюю правку? Никаких обид там нет, это было бы совершенно понятно с вашей точки зрения :)
Проверьте здесь:
Примечание: Очевидно, что это простой пример объяснения просто как создать 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
}
int main
это правильная вещь, но gcc
и другие компиляторы компилируют это в правильно работающие программы с 1990-х годов. Объяснено довольно хорошо здесь: eskimo.com/~scs/readings/voidmain.960823.html - это в основном «фича», я так понимаю.
Приложение относительно платформ 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
}
}
Или вы можете перевести терминал в сырой режим, например так:
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) { / * Ваш код здесь. * / }
@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
...
(void)_;
... Какова его цель? Это так, что компилятор не предупреждает о неиспользуемой переменной?
Что касается существующих ответов, обратите внимание, что обработка сигналов зависит от платформы. Например, 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);
}