C语言中的信号处理

21

如何在C中实现Ctrl-C和Ctrl-D的信号处理...所以如果按下Ctrl-C,则程序将忽略并尝试再次获取用户输入...如果按下Ctrl-D,则程序将终止...

我的程序如下:

int main(){
 char msg[400];

 while(1){
   printf("Enter: ");
   fgets(msg,400,stdin);
   printf("%s\n",msg); 
 }
}

谢谢,

Dave


3
我不建议这样做。我很难想象有很多情况下程序重写 Ctrl-C 的终止功能是一个好主意,除了像 SSH 这样的远程连接工具。 - Daniel Bingham
这只是为了理解如何处理信号。 - Dave
2
任何类型的解释器程序在运行时应该捕获ctrl-C以中止托管程序。 - Potatoswatter
5个回答

33

当处理 POSIX 信号时,您有两种手段。首先是简单(但不建议使用)的方式 signal()。其次是更加优雅、当前但复杂的方式 sigaction()。除非您发现需要使用的平台上不支持 sigaction(),否则请使用 sigaction()。

glibc 手册中的这一章节解释了这两种方式之间的差异,并给出了如何同时使用它们的良好示例代码。它还列出了可以处理的信号,推荐如何处理它们,并深入介绍了如何判断任何给定信号当前是否正在被处理。但由于篇幅较长,我不想在此回答中粘贴所有的代码,因此提供了链接。

值得花费一到两个小时阅读这些链接并透彻理解示例。信号处理(特别是在守护程序中)非常重要。一个好的程序应该处理所有可以处理的致命信号(例如 SIGHUP),并明确忽略可能没有使用到的信号(例如 SIGUSR1 / SIGUSR2)。

另外,学习普通信号和实时信号之间的区别也是有益的,至少要理解内核如何合并前者而不是后者。

一旦您解决了这个问题,可能会有意愿编写一组易于修改的函数来处理信号并重复使用该代码。很抱歉没有提供一个快速而简单的代码片段来展示如何解决您的即时需求,但这不是一个快速而简单的主题 :)

15

首先,Ctrl+D是一个EOF指示器,无法捕获。当程序等待输入时,按下Ctrl+D表示文件结束(EOF),不再期望更多的输入。另一方面,使用Ctrl+C来终止程序 - 这是SIGINT信号,可以通过以下方式捕获:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
#include <stdarg.h>

static void signal_handler(int);
static void cleanup(void);
void init_signals(void);
void panic(const char *, ...);

struct sigaction sigact;
char *progname;

int main(int argc, char **argv){
    char *s;
    progname = *(argv);
    atexit(cleanup);
    init_signals();
    // do the work
    exit(0);
}

void init_signals(void){
    sigact.sa_handler = signal_handler;
    sigemptyset(&sigact.sa_mask);
    sigact.sa_flags = 0;
    sigaction(SIGINT, &sigact, (struct sigaction *)NULL);
}

static void signal_handler(int sig){
    if (sig == SIGINT) panic("Caught signal for Ctrl+C\n");
}

void panic(const char *fmt, ...){
    char buf[50];
    va_list argptr;
    va_start(argptr, fmt);
    vsprintf(buf, fmt, argptr);
    va_end(argptr);
    fprintf(stderr, buf);
    exit(-1);
}

void cleanup(void){
    sigemptyset(&sigact.sa_mask);
    /* Do any cleaning up chores here */
}

SIG_IGN处理程序非常适合OP的需求。 - outis
2
为什么sigact是全局变量?cleanup为什么要与它交互;如果要更改sigact以影响程序的行为,您必须再次调用sigaction()。 - Jonathan Leffler
@JonathanLeffler:您能更好地解释一下为什么将sigact声明为全局变量不是一个好主意吗?我想要理解这一点。谢谢。 - Kyrol
2
@Kyrol:它不需要成为全局变量;它可以完全作为函数中的局部变量,因为它仅在函数内部使用。当调用sigaction()时,内核会从结构体中复制所需的信息;之后无需保留该值。 - Jonathan Leffler

2

这是一个用于处理按下 Ctrl+c 时信号的程序。

signal 函数的语法为:signal(信号名称, 函数名称);

#include<stdio.h>
#include<signal.h>  // for handling signal 

void signal_handler() 
{
    printf("Signal Handled here\n");
}

main() 
{
    printf("In main function..\n");

    // SIGINT is signal name create  when Ctrl+c will pressed 
    signal(SIGINT,signal_handler);  

    sleep(15);

    printf("In main after called from signal_handle \n");

}

signal()已被弃用,应尽可能避免使用,几乎所有现代平台(除了一些嵌入式平台)都提供了sigaction() - Tim Post
1
@TimPost signal()从未被弃用。它提供的控制比sigaction()少。但这并不等同于被弃用。 - P.P
@P.P signal()可能尚未正式弃用,但强烈不建议使用。从man 2 signal中可以看到:警告:signal()的行为在UNIX版本之间有所不同,并且在不同版本的Linux中也有所不同。避免使用它:改用sigaction(2)。请参见下面的可移植性。 它继续说道:signal()的唯一可移植用途是将信号的处理方式设置为SIG_DFL或SIG_IGN。当使用signal()来建立信号处理程序时,其语义在系统之间有所不同(并且POSIX.1明确允许此变化);不要将其用于此目的。 - Fonic
1
@Fonic 正确的,signal 没有被弃用 ;-) - P.P

2
在您的示例中,似乎您根本不需要CTRL-C处理。 "signal(SIGINT,SIG_IGN)" 对于您来说已经足够了,除非您的应用程序必须处理来自其他来源的SIGINT。 CTRL-D通常不会生成信号,它只是传达EOF条件。 通常可以使用termios库(也可在此处)控制终端的行为(我们正在谈论控制台输入吗?)。 您可以启用,重新定义或禁用“中断”字符(CTRL-C),EOF字符和许多其他字符(XON,XOFF,调制解调器控制...)
问候

0
#include<signal.h>
#include<unistd.h>
#include<stdio.h>
void signal_catch()
{
    printf("hi,Your signal catched Here");  
}
int main()
{
    signal(SIGINT,signal_catch);
//press ctrl+c
    sleep(10);
    return 0;
}//end main

//if you want to simply ignore ctrl+c interrupt use following code in main

int main()
{
    signal(SIGINT,SIG_IGN);
    sleep(100);
    return 0;
}//end main  
//this program  wont accept ctrl+c interrupt for 100 seconds. 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接