8

我正在用 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()makefgets()停止等待输入?

编辑:为我的catch_signal函数添加了代码,并且根据评论之一,使用sigaction而不是signal,但问题仍然存在。

4

2 回答 2

4

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

(如 Retr0spectrum 所述) fgets() 函数经常进行系统调用,例如 read()。如果检测到信号,系统调用可以终止。在这个问题的情况下, fgets() 函数进行了系统调用(可能是 read() 系统调用)以从标准输入读取字符。SIGALRM 导致系统调用终止,并将 errno 设置为 EINTR。这也会导致 fgets() 函数终止,而不读取任何字符。

这并不罕见。这就是操作系统实现信号的方式。

为了避免这个问题,我经常将 fgets() 函数包装在一个循环中,如下所示:

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

这将要求您:#include <stdio.h>

(正如 TonyB 所建议的那样)。

于 2017-09-11T00:07:14.657 回答
3

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

  1. 这是 Unix 过去的做法,因为它更容易在操作系统中实现。(一方面这听起来有点蹩脚,但“不要为难的东西出汗”的态度首先是 Unix 成功的部分原因。这是 Richard P. Gabriel 的史诗Worse Is Better的主题散文。)

  2. 如果您想要的话,它可以很容易地实现超时并放弃的读取。(请参阅我对其他问题的回答。)

但是,正如其他评论和答案所讨论的那样,打断行为有些过时了。大多数现代系统(至少是 Unix 和 Linux)现在会自动重新启动中断的系统调用,例如read,或多或少如您所愿。(也正如在其他地方指出的那样,如果您知道自己在做什么,则可以在这两种行为之间进行选择。)

但最终,这是一个灰色地带。我很确定 C 标准未指定或未定义实现定义或未定义如果您使用警报或其他信号中断系统调用会发生什么。

于 2017-09-11T03:04:42.480 回答