Нет такой вещи, как перехват сигналов в C .... или, по крайней мере, так я думал, пока не прочитал стандарт C99. Оказывается, в C определена обработка сигналов, но Ctrl-C не обязан создавать какой-либо конкретный сигнал или сигнал вообще. В зависимости от вашей платформы это может быть невозможно.
JeremyP
1
Обработка сигналов в основном зависит от реализации. На платформах * nix используйте <signal.h>, и, если вы работаете в OSX, вы можете воспользоваться GCD, чтобы сделать вещи еще проще ~.
Дилан Люк
Ответы:
205
С обработчиком сигнала.
Вот простой пример, переворачивающий boolиспользуемый в main():
Редактировать в июне 2017 года : кому это может касаться, особенно тем, у кого есть ненасытное желание отредактировать этот ответ. Смотри, я написал этот ответ семь лет назад. Да, языковые стандарты меняются. Если вы действительно должны улучшить мир, пожалуйста, добавьте свой новый ответ, но оставьте мой как есть. Поскольку в ответе указано мое имя, я бы предпочел, чтобы в нем также содержались мои слова. Спасибо.
Давайте отметим, что нам нужно #include <signal.h>, чтобы это работало!
kristianlm
13
статический Это должно быть bool volatile keepRunning = true;на 100% безопасно. Компилятор может свободно кэшировать данные keepRunningв регистре, и volatile предотвращает это. На практике это, скорее всего, также может работать без ключевого слова volatile, когда цикл while вызывает хотя бы одну не встроенную функцию.
Йоханнес Оверманн
2
@DirkEddelbuettel Я исправлен, я думал, что мои улучшения будут отражать ваши первоначальные намерения больше, извините, если этого не произошло. В любом случае, большая проблема заключается в том, что, поскольку ваш ответ пытается быть достаточно универсальным, а IMO должен предоставить фрагмент, который также работает при асинхронных прерываниях: я бы либо использовал, sig_atomic_tлибо atomic_boolнапечатал там. Я просто пропустил это. Теперь, поскольку мы говорим: хотите, чтобы я откатил мою последнюю правку? Никаких обид там нет, это было бы совершенно понятно с вашей точки зрения :)
Питер Варо
2
Это намного лучше!
Дирк Эддельбюттель
2
@JohannesOvermann Не то чтобы я хотел придираться, но, строго говоря, не имеет значения, вызывает ли код какие-либо не встроенные функции или нет, поскольку только при пересечении барьера памяти компилятору не разрешается полагаться на кэшированное значение. Блокировка / разблокировка мьютекса была бы таким барьером памяти. Поскольку переменная является статической и, следовательно, не видна за пределами текущего файла, компилятор может предположить, что функция никогда не сможет изменить свое значение, если вы не передадите ссылку на эту переменную в функцию. Настоятельно рекомендуется в любом случае здесь изменчиво.
Примечание: Очевидно, что это простой пример объяснения просто как создать CtrlCобработчик, но как всегда есть правила , которые должны быть послушны, чтобы не сломать что - то другое. Пожалуйста, прочитайте комментарии ниже.
Пример кода сверху:
#include<stdio.h>#include<signal.h>#include<stdlib.h>voidINThandler(int);int main(void){
signal(SIGINT,INThandler);while(1)
pause();return0;}voidINThandler(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 - это в основном «фича», я так понимаю.
icyrock.com
1
@ icyrock.com: Все очень верно (в отношении void main () в C), но при публичном размещении, вероятно, также следует избегать дискуссий вообще, используя int main (), чтобы не отвлекать внимание от основного.
Клиффорд
21
В этом есть огромный недостаток. Вы не можете безопасно использовать printf в содержимом обработчика сигналов. Это нарушение безопасности асинхронного сигнала. Это потому, что printf не реентерабелен. Что произойдет, если программа была в процессе использования printf при нажатии Ctrl-C, и ваш обработчик сигналов начинает использовать ее одновременно? Подсказка: скорее всего, сломается. write и fwrite одинаковы для использования в этом контексте.
Дилан Люк
2
@ icyrock.com: Выполнение чего-либо сложного в обработчике сигналов может вызвать головную боль. Особенно с использованием системы IO.
Мартин Йорк,
2
@ Stacker Спасибо - я думаю, это того стоит. Если кто-то случайно наткнется на этот код в будущем, лучше сделать его максимально правильным, независимо от темы вопроса.
icyrock.com
30
Приложение относительно платформ UN * X.
Согласно signal(2)справочной странице по GNU / Linux, поведение signalне так переносимо, как sigaction:
Поведение signal () варьируется в разных версиях UNIX, а также исторически различалось в разных версиях Linux. Избегайте его использования: используйте вместо этого sigaction (2).
В системе V система не блокировала доставку дальнейших экземпляров сигнала, и доставка сигнала сбрасывала обработчик к стандартному. В BSD семантика изменилась.
Следующий вариант предыдущего ответа Дирка Эддельбюттеля использует sigactionвместо signal:
Теперь должно быть возможно читать Ctrl+ Cнажатия клавиш, используя fgetc(stdin). Остерегайтесь использовать это, хотя, потому что вы не можете Ctrl+ Z, Ctrl+ Q, Ctrl+ S, и т. Д. Как обычно больше.
@Peter Varo обновил ответ Дирка, но Дирк отклонил изменение. Вот новый ответ Питера:
Хотя приведенный выше фрагмент является правильным c89Например, следует использовать более современные типы и гарантии, предоставляемые более поздними стандартами, если это возможно. Поэтому здесь есть более безопасная и современная альтернатива для тех, кто ищетc99 и c11 соответствующая реализация:
#include<signal.h>#include<stdlib.h>#include<stdio.h>staticvolatilesig_atomic_t keep_running =1;staticvoid 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...
Я только что нашел этот ответ и собирался вставить эту ссылку здесь! Спасибо :)
Луис Пауло
5
Что касается существующих ответов, обратите внимание, что обработка сигналов зависит от платформы. Например, 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 processwhile(1)
sleep(1);return0;}
Функция sig_handler проверяет, равно ли значение передаваемого аргумента SIGINT, затем выполняется printf.
Ответы:
С обработчиком сигнала.
Вот простой пример, переворачивающий
bool
используемый вmain()
:Редактировать в июне 2017 года : кому это может касаться, особенно тем, у кого есть ненасытное желание отредактировать этот ответ. Смотри, я написал этот ответ семь лет назад. Да, языковые стандарты меняются. Если вы действительно должны улучшить мир, пожалуйста, добавьте свой новый ответ, но оставьте мой как есть. Поскольку в ответе указано мое имя, я бы предпочел, чтобы в нем также содержались мои слова. Спасибо.
источник
bool volatile keepRunning = true;
на 100% безопасно. Компилятор может свободно кэшировать данныеkeepRunning
в регистре, и volatile предотвращает это. На практике это, скорее всего, также может работать без ключевого слова volatile, когда цикл while вызывает хотя бы одну не встроенную функцию.sig_atomic_t
либоatomic_bool
напечатал там. Я просто пропустил это. Теперь, поскольку мы говорим: хотите, чтобы я откатил мою последнюю правку? Никаких обид там нет, это было бы совершенно понятно с вашей точки зрения :)Проверьте здесь:
Примечание: Очевидно, что это простой пример объяснения просто как создать CtrlCобработчик, но как всегда есть правила , которые должны быть послушны, чтобы не сломать что - то другое. Пожалуйста, прочитайте комментарии ниже.
Пример кода сверху:
источник
int main
это правильная вещь, ноgcc
и другие компиляторы компилируют это в правильно работающие программы с 1990-х годов. Объяснено довольно хорошо здесь: eskimo.com/~scs/readings/voidmain.960823.html - это в основном «фича», я так понимаю.Приложение относительно платформ UN * X.
Согласно
signal(2)
справочной странице по GNU / Linux, поведениеsignal
не так переносимо, какsigaction
:В системе V система не блокировала доставку дальнейших экземпляров сигнала, и доставка сигнала сбрасывала обработчик к стандартному. В BSD семантика изменилась.
Следующий вариант предыдущего ответа Дирка Эддельбюттеля использует
sigaction
вместоsignal
:источник
volatile sig_atomic_t
чтобы быть четко определеныИли вы можете перевести терминал в сырой режим, например так:
Теперь должно быть возможно читать Ctrl+ Cнажатия клавиш, используя
fgetc(stdin)
. Остерегайтесь использовать это, хотя, потому что вы не можете Ctrl+ Z, Ctrl+ Q, Ctrl+ S, и т. Д. Как обычно больше.источник
Установите ловушку (вы можете перехватить несколько сигналов одним обработчиком):
Обрабатывайте сигнал как хотите, но помните об ограничениях и ошибках:
источник
@Peter Varo обновил ответ Дирка, но Дирк отклонил изменение. Вот новый ответ Питера:
Хотя приведенный выше фрагмент является правильным c89Например, следует использовать более современные типы и гарантии, предоставляемые более поздними стандартами, если это возможно. Поэтому здесь есть более безопасная и современная альтернатива для тех, кто ищетc99 и c11 соответствующая реализация:
Более того:
источник
(void)_;
... Какова его цель? Это так, что компилятор не предупреждает о неиспользуемой переменной?Что касается существующих ответов, обратите внимание, что обработка сигналов зависит от платформы. Например, Win32 обрабатывает гораздо меньше сигналов, чем операционные системы POSIX; смотрите здесь . Хотя SIGINT объявлен в signal.h на Win32, см. Примечание в документации, объясняющее, что он не будет делать то, что вы ожидаете.
источник
Функция sig_handler проверяет, равно ли значение передаваемого аргумента SIGINT, затем выполняется printf.
источник
Это просто распечатать перед выходом.
источник