2017-09-10 100 views
7

我正在玩C中的信号。我的主要功能基本上要求使用fgets(name, 30, stdin)进行一些输入,然后坐在那里等待。我用alarm(3)设置了一个闹钟,然后我重新分配了SIGALRM来调用函数myalarm,该函数简单地调用system("say PAY ATTENTION")。但是在闹铃响起后,fgets()停止等待输入,我的主要fn继续。即使我将myalarm更改为只设置了一些变量并且什么也不做,会发生这种情况。为什么alarm()会导致fgets()停止等待?

void myalarm(int sig) { 
    //system("say PAY ATTENTION"); 
    int x = 0; 
} 

int catch_signal(int sig, void (*handler)(int)) { // when a signal comes in, "catch" it and "handle it" in the way you want 
    struct sigaction action; // create a new sigaction 
    action.sa_handler = handler; // set it's sa_handler attribute to the function specified in the header 
    sigemptyset(&action.sa_mask); // "turn all the signals in the sa_mask off?" "set the sa_mask to contian no signals, i.e. nothing is masked?" 
    action.sa_flags = 0; // not sure, looks like we aren't using any of the available flags, whatever they may be 

    return sigaction(sig, &action, NULL); // here is where you actually reassign- now when sig is received, it'll do what action tells it 
} 

int main() { 

    if(catch_signal(SIGINT, diediedie)== -1) { 
     fprintf(stderr, "Can't map the SIGINT handler"); 
     exit(2); 
    } 

    if(catch_signal(SIGALRM, myalarm) == -1) { 
     fprintf(stderr, "Can't map the SIGALAM handler\n"); 
     exit(2); 
    } 

    alarm(3); 

    char name[30]; 
    printf("Enter your name: "); 
    fgets(name, 30, stdin); 

    printf("Hello, %s\n", name); 

    return 0; 

} 

为什么alarm()使fgets()停止等待输入?

编辑:为我的catch_signal函数添加了代码,并且根据其中一个注释,使用sigaction而不是signal,但问题依然存在。

+5

请参阅'man 7 signal',子标题为“通过信号处理程序中断系统调用和库函数” – Retr0id

+0

我看到这一点:“如果对信号处理程序中断了以下某个接口的阻塞呼叫,如果使用SA_RESTART标志,则在信号处理程序返回后调用将自动重新启动;否则调用将失败,并显示错误EINTR ...“并且fgets()未被列为可重新启动的函数之一。我试图做的是不可能的? –

+0

'fgets()'在引擎盖下使用'read'系统调用。顺便说一句,在我的系统上它不会中断(Arch Linux x86_64) – Retr0id

回答

4

答案很可能是OS /系统特定的。 (如Retr0spectrum所述)fgets()函数通常会进行系统调用,例如read()。如果检测到信号,系统调用可能会终止。在这个问题的情况下,fgets()函数已经进行了系统调用(可能是read()系统调用)从stdin读取一个字符。 SIGALRM导致系统调用终止,并将errno设置为EINTR。这也会导致fgets()函数终止,而不读取任何字符。

这并不罕见。这就是操作系统如何实现信号。

为了避免这个问题,我会经常换与fgets()函数在这样的循环:

do { 
    errno=0; 
    fgets(name, 30, stdin); 
    } while(EINTR == errno); 

它会要求你:#include <stdio.h>

(如TonyB建议)。

+0

我会在*“fgets()函数经常使系统调用”* ;-)后添加[需要的引用] – spectras

+0

这里的循环是错误的。 Stdio功能不能像那样重新启动;错误后未指定缓冲区内容和文件位置。如果您使用的是stdio,并且不希望信号成为致命错误,您必须**将'sigaction'与'SA_RESTART'一起使用。 –

3

至于为什么报警信号中断读的的问题,有两个方面的原因:

  1. 它的Unix用来做什么的方式,这是因为它更容易实现在OS中。 (一方面这听起来有些蹩脚,但“不要汗流hard背”的态度对于Unix的成功起到了一定的作用。这是Richard P. Gabriel的史诗主题更糟的是更好的短文。)

  2. 它可以很容易地实现一个读超时和放弃,如果这就是你想要的。 (见我的回答this other question

但是,正如其他意见和解答讨论,中断的行为是有点过时;大多数现代系统(至少Unix和Linux)现在会自动重启一个中断的系统调用,如read,或多或少地按照您的意愿重启。 (也正如其他地方指出的那样,如果你知道自己在做什么,你可能可以在两种行为之间进行选择。)

最后,虽然是灰色地带,我非常确定C标准没有指定它,或者实现定义或未定义如果您使用警报或其他信号中断系统调用会发生什么情况。

相关问题