CでCtrl+をどのようにキャッチCしますか?
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年6月に編集:関係者、特にこの回答を編集したくてたまらない衝動を持つ人々。見て、私は7年前にこの答えを書きました。はい、言語基準が変わります。本当に世界を良くする必要がある場合は、新しい答えを追加してください。答えには私の名前が含まれているので、自分の言葉を含めることもできます。ありがとうございました。
bool volatile keepRunning = true;
100%安全である必要があります。コンパイラはkeepRunning
レジスタに自由にキャッシュでき、揮発性はこれを防止します。実際には、whileループが少なくとも1つの非インライン関数を呼び出すときに、volatileキーワードがなくても機能する可能性があります。
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のmanページによると、の動作はの動作signal
ほど移植性がありませんsigaction
。
signal()の動作はUNIXのバージョンによって異なり、歴史的にはLinuxのバージョンによっても異なります。その使用は避けてください。代わりにsigaction(2)を使用してください。
System Vでは、システムはシグナルのそれ以上のインスタンスの配信をブロックせず、シグナルの配信はハンドラーをデフォルトのインスタンスにリセットします。BSDでは、セマンティクスが変更されました。
Dirk Eddelbuettelによる以前の回答の次のバリエーションは、の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
}
}
または、次のように端末をrawモードにすることもできます。
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などは通常のように使用できなくなるため、これを使用する場合は注意してください。
トラップを設定します(1つのハンドラーで複数のシグナルをトラップできます)。
シグナル(SIGQUIT、my_handler); シグナル(SIGINT、my_handler);
信号は必要に応じて処理しますが、制限と注意点に注意してください。
void my_handler(int sig) { / *ここにあなたのコード。* / }
@ピーターヴァロはダークの回答を更新しましたが、ダークは変更を拒否しました。これがピーターの新しい答えです:
上記のスニペットは正しいですが 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はWin32のsignals.hで宣言されていますが、期待どおりに動作しないことを説明しているドキュメントのメモを参照してください。
#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);
}