如何在C中捕获 Ctrl + C ?
答案 0 :(得分:181)
使用信号处理程序。
以下是翻转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月编辑:可能会引起关注,尤其是那些对编辑此答案抱怨无法满足的人。看,我在七年前写了七这个答案。是的,语言标准会发生变化如果你真的必须改善这个世界,请添加你的新答案,但保留原样。由于答案上有我的名字,我更喜欢它也包含我的话。谢谢。
答案 1 :(得分:42)
点击此处:
注意:显然,这是一个简单的示例,解释只是如何设置 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
}
答案 2 :(得分:28)
关于UN * X平台的附录。
根据GNU / Linux上的signal(2)
手册页,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
}
}
答案 3 :(得分:11)
或者您可以将终端设置为原始模式,如下所示:
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);
现在应该可以使用fgetc(stdin)
读取 Ctrl + C 击键。请注意使用它,因为你不能 Ctrl + Z , Ctrl + Q , Ctrl < / kbd> + S 等,正常情况下也是如此。
答案 4 :(得分:10)
设置陷阱(您可以使用一个处理程序捕获多个信号):
signal (SIGQUIT, my_handler); signal (SIGINT, my_handler);
随时处理信号,但要注意限制和陷阱:
void my_handler (int sig) { /* Your code here. */ }
答案 5 :(得分:4)
关于现有答案,请注意信号处理取决于平台。例如,Win32处理的信号远少于POSIX操作系统; see here。虽然在Win32上的signals.h中声明了SIGINT,但请参阅文档中的说明,该说明解释说它不会达到您的预期效果。
答案 6 :(得分:3)
#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。
答案 7 :(得分:2)
这只是在退出前打印。
#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);
}
答案 8 :(得分:2)
@Peter Varo更新了Dirk的答案,但Dirk拒绝了更改。这是彼得的新答案:
尽管以上摘录是正确的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
的对象分配值来实现...