为什么 alarm() 会导致 fgets() 停止等待?
Why does alarm() cause fgets() to stop waiting?
我正在用 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
,但问题仍然存在。
答案很可能是 OS/system 具体的。
(如Retr0spectrum所述) fgets()函数经常进行系统调用,例如read()。如果检测到信号,系统调用可以终止。在这个问题的例子中,fgets() 函数进行了系统调用(可能是 read() 系统调用)以从标准输入读取一个字符。 SIGALRM 导致系统调用终止,并将 errno 设置为 EINTR。这也会导致 fgets() 函数终止,而不读取任何字符。
这并不罕见。这就是 OS 实现信号的方式。
为了避免这个问题,我经常将 fgets() 函数包装在一个循环中,如下所示:
do {
errno=0;
fgets(name, 30, stdin);
} while(EINTR == errno);
它将要求您:#include <stdio.h>
(根据 TonyB 的建议)。
关于为什么报警信号中断读取的问题,有两个原因:
这是 Unix 过去的做法,因为它在 OS 中更容易实现。 (一方面,这听起来有点蹩脚,但 "don't sweat the hard stuff" 态度首先是 Unix 成功的部分原因。这是 Richard P. Gabriel 的史诗 Worse Is 的主题更好论文。)
它可以轻松实现超时并放弃的读取,如果这是您想要的。 (参见我对 this other question 的回答。)
但是,正如其他评论和答案所讨论的那样,中断行为有些过时了;大多数现代系统(Unix 和 Linux,至少)现在自动重启中断的系统调用,例如 read
,或多或少如您所愿。 (也正如其他地方指出的那样,如果您知道自己在做什么,您也许可以 select 在两种行为之间。)
不过,归根结底,这是一个灰色地带;我很确定 C 标准未指定或 implementation-defined 或未定义如果您用警报或其他信号中断系统调用会发生什么。
我正在用 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
,但问题仍然存在。
答案很可能是 OS/system 具体的。
(如Retr0spectrum所述) fgets()函数经常进行系统调用,例如read()。如果检测到信号,系统调用可以终止。在这个问题的例子中,fgets() 函数进行了系统调用(可能是 read() 系统调用)以从标准输入读取一个字符。 SIGALRM 导致系统调用终止,并将 errno 设置为 EINTR。这也会导致 fgets() 函数终止,而不读取任何字符。
这并不罕见。这就是 OS 实现信号的方式。
为了避免这个问题,我经常将 fgets() 函数包装在一个循环中,如下所示:
do {
errno=0;
fgets(name, 30, stdin);
} while(EINTR == errno);
它将要求您:#include <stdio.h>
(根据 TonyB 的建议)。
关于为什么报警信号中断读取的问题,有两个原因:
这是 Unix 过去的做法,因为它在 OS 中更容易实现。 (一方面,这听起来有点蹩脚,但 "don't sweat the hard stuff" 态度首先是 Unix 成功的部分原因。这是 Richard P. Gabriel 的史诗 Worse Is 的主题更好论文。)
它可以轻松实现超时并放弃的读取,如果这是您想要的。 (参见我对 this other question 的回答。)
但是,正如其他评论和答案所讨论的那样,中断行为有些过时了;大多数现代系统(Unix 和 Linux,至少)现在自动重启中断的系统调用,例如 read
,或多或少如您所愿。 (也正如其他地方指出的那样,如果您知道自己在做什么,您也许可以 select 在两种行为之间。)
不过,归根结底,这是一个灰色地带;我很确定 C 标准未指定或 implementation-defined 或未定义如果您用警报或其他信号中断系统调用会发生什么。