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