为什么alarm()导致fgets()停止等待?

时间:2017-09-10 22:45:19

标签: c fgets alarm sigaction

我正在玩C中的信号。我的主要功能基本上是使用fgets(name, 30, stdin)请求输入,然后坐在那里等待。我使用alarm(3)设置闹钟,然后我重新分配SIGALRM来调用只调用myalarm的函数system("say PAY ATTENTION")。但是在闹钟响起后,fgets()停止等待输入,我的主fn继续。即使我将myalarm更改为仅设置某个变量并且不对其执行任何操作,也会发生这种情况。

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,但问题仍然存在。

2 个答案:

答案 0 :(得分:4)

答案很可能是特定于操作系统/系统。

(如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所建议的那样)。

答案 1 :(得分:3)

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

  1. 这是Unix用来做这件事的方式,因为它在操作系统中更容易实现。 (一方面,这听起来有点蹩脚,但是不要狠狠地扼杀辛苦的东西&#34;态度首先要归功于Unix的成功。这是理查德的话题P. Gabriel的史诗更糟糕的是文章。)

  2. 如果符合您的要求,可以轻松实现超时和放弃的阅读。 (参见我对this other question的回答。)

  3. 但是,正如其他评论和答案所讨论的那样,中断行为有点过时;大多数现代系统(至少是Unix和Linux)现在会根据您的需要或多或少自动重启中断的系统调用,例如read。 (另外在其他地方指出,如果你知道自己在做什么,你可以在两种行为之间进行选择。)

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