【发布时间】:2015-06-26 15:39:10
【问题描述】:
上周我们举办了一场讲座,内容涉及操作系统(在本例中为 Linux,在本例中我们的学校服务器使用 SUSE Linux 11)如何处理中断。需要注意的一点是,对于大多数信号,您可以捕获中断并定义自己的信号处理程序来运行,而不是默认运行。我们用一个例子来说明这一点,我发现起初在我看来很有趣的行为。代码如下:
#include <stdio.h>
#include <signal.h>
#define INPUTLEN 100
main(int ac, char *av[])
{
void inthandler (int);
void quithandler (int);
char input[INPUTLEN];
int nchars;
signal(SIGINT, inthandler);
signal(SIGQUIT, quithandler);
do {
printf("\nType a message\n");
nchars = read(0, input, (INPUTLEN - 1));
if ( nchars == -1)
perror("read returned an error");
else {
input[nchars] = '\0';
printf("You typed: %s", input);
}
}
while(strncmp(input, "quit" , 4) != 0);
}
void inthandler(int s)
{
printf(" Received Signal %d ....waiting\n", s);
int i = 0;
for(int i; i<3; ++i){
sleep(1);
printf("inth=%d\n",i);
}
printf(" Leaving inthandler \n");
}
void quithandler(int s)
{
printf(" Received Signal %d ....waiting\n", s);
for(int i; i<7; ++i){
sleep(1);
printf("quith=%d\n",i);
} printf(" Leaving quithandler \n");
}
所以,在运行这段代码时,我希望是这样的:
- 运行代码.... ^C
- 进入inthandler,执行循环,点击^\
- 退出inthandler,进入quithandler,执行quithandler循环
- ^C 返回到 inhandler。如果我在 inthandler 中时再次执行 ^C,则忽略连续的 inthandler 信号,直到当前的 inthandler 完成处理。
根据观察,我发现一些信号似乎是嵌套的、2 队列深度的“调度”。例如,如果我快速连续输入以下中断:
- ^C, ^\, ^C, ^\, ^\, ^C
我将从代码中收到以下行为/输出:
^CReceived signal 2 ....waiting
^\Received Signal 3 ....waiting
^C^\^\^C quith=0
quith=1
quith=2
quith=3
quith=4
quith=5
quith=6
quith=7
Leaving quithandler
Received Signal 3 ....waiting
quith=1
quith=2
quith=3
quith=4
quith=5
quith=6
quith=7
Leaving quithandler
inth=0
inth=1
inth=2
inth=3
Leaving inthandler
Received Signal 2 ....waiting
inth=0
inth=1
inth=2
inth=3
Leaving inthandler
换句话说,它似乎是这样处理的:
- 接收第一个 ^C 信号
- 接收 ^\ 信号,“延迟” inthandler 并进入 quithandler
- 接收下一个 ^C 信号,但因为我们已经“嵌套”在一个 inthandler 中,所以将它放在 inthandler“队列”的后面
- 接收 quithandler,放在 quithandler 队列的后面。
- 执行退出处理程序,直到队列为空。忽略第三个退出处理程序,因为它的队列深度似乎只有 2。
- 离开quithandler,执行剩下的2个inhandler。忽略最终的 inthandler,因为队列深度为 2。
我向我的教授展示了这种行为,他似乎同意“嵌套 2 队列深度”行为是正在发生的事情,但我们不能 100% 确定原因(他来自硬件背景并且刚刚开始教这门课)。我想在 SO 上发帖,看看是否有人可以阐明 Linux 处理这些信号的原因/方式,因为我们不太期待某些行为,即嵌套。
我认为我写的测试用例应该足以说明发生了什么,但这里有一堆额外测试用例的截图:
http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF
我想留下额外的测试用例作为链接,因为它们有点大屏幕截图。
谢谢!
【问题讨论】:
-
为了获得最佳效果,函数原型应该在任何函数之外。
-
你应该显示你运行的代码,而不是它的近似值。您的“退出”处理程序代码打印“inth”,但记录显示打印“quith”的内容。
-
参见How to avoid using
printf()in a signal handler? 此外,与sigaction()相比,您对signal()所发生的事情的控制有限。见What is the difference betweensignal()andsigaction()? -
发布的代码编译不干净。出于多种原因,从 main 的两个未使用参数开始:ac 和 av。建议编译启用所有警告(或至少'-Wall -Wextra -pedantic')然后修复警告,然后重新发布代码。首先,代码缺少几个头文件 string.h 和 unistd.h
-
@JonathanLeffler 抱歉 - 我无法让 VPN 在我的桌面上运行(我在这里输入这篇文章,可能是因为我在 W10TP 上)所以我在重写时手动编写了其中的一些部分问题 - 我不小心写了 inth 而不是 Quith。我已经解决了。至于 printf(),这不是原始示例附带的(请参见此处:pastebin.com/k1CFRzPx 我只是想能够看到输出以确定它在做什么。如果我只使用睡眠。printf 是否与它的工作方式有关?