Як один лов Ctrl+ Cу С?
Як один лов Ctrl+ 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
в реєстрі, і непостійне запобіжить цьому. На практиці це, швидше за все, може працювати і без мінливого ключового слова, коли цикл 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
:
Поведінка сигналу () варіюється в різних версіях 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);
Обробляйте сигнал як завгодно, але будьте в курсі обмежень та отриманих даних:
недійсний 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 оголошується в сигналі.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);
}