我们上周进行了一次演讲,其中涉及操作系统(在本例中为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");
}
因此,在运行此代码时,我期待这样的事情:
我发现基于观察的东西,似乎是一个嵌套的,2队列深度的&#34;调度&#34;信号。例如,如果我快速连续输入以下中断:
我将从代码中收到以下行为/输出:
^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
换句话说,它似乎是这样处理的:
我向我的教授展示了这种行为,他似乎同意&#34;嵌套2队列深度&#34;行为是正在发生的事情,但我们并不是100%肯定为什么(他来自硬件背景,而且刚刚开始教授这门课程)。我想发布SO以了解是否有人可以阐明Linux处理这些信号的原因和方式,因为我们并不期待某些行为,即嵌套。
我认为我写的测试用例应足以说明发生了什么,但这里有一些其他测试用例的截图:
http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF
我希望将其他测试用例保留为链接,因为它们是一种大型屏幕截图。
谢谢!
答案 0 :(得分:9)
规则(针对非实时信号,例如您正在使用的SIGQUIT
和SIGINT
):
待处理状态为二进制 - 信号处于暂挂或未处理状态。如果在屏蔽的情况下多次引发信号,则在取消屏蔽时仍然只会传递一次。
所以你的例子中会发生什么:
SIGINT
被引发,inthandler()
信号处理程序开始执行,SIGINT
被屏蔽。SIGQUIT
,quithandler()
信号处理程序开始执行(中断inthandler()
)并屏蔽了SIGQUIT
。SIGINT
,将SIGINT
添加到待处理信号集中(因为它已被屏蔽)。SIGQUIT
,将SIGQUIT
添加到待处理信号集(因为它被屏蔽)。 SIGQUIT
,但没有任何结果,因为SIGQUIT
已经处于待处理状态。SIGINT
,但没有任何结果,因为SIGINT
已经处于待处理状态。quithandler()
完成执行,SIGQUIT
被取消屏蔽。由于SIGQUIT
处于待处理状态,因此会传递,quithandler()
再次开始执行(再次屏蔽SIGQUIT
)。quithandler()
第二次完成执行,SIGQUIT
被取消屏蔽。 SIGQUIT
未处于待处理状态,因此inthandler()
会继续执行(SIGINT
仍然屏蔽)。inthandler()
完成执行,SIGINT
被取消屏蔽。由于SIGINT
处于待处理状态,因此会传递,inthandler()
再次开始执行(再次屏蔽SIGINT
)。inthandler()
第二次完成执行,SIGINT
被取消屏蔽。然后主要功能继续执行。在Linux上,您可以通过检查/proc/<PID>/status
来查看进程的当前屏蔽和待处理信号集。屏蔽信号显示在SigBlk:
位掩码和SigPnd:
位掩码中的待处理信号中。
如果使用sigaction()
而不是signal()
安装信号处理程序,则可以指定SA_NODEFER
标志,以请求在处理程序执行时不屏蔽信号。您可以在程序中尝试这个 - 使用一个或两个信号 - 并尝试预测输出的样子。
答案 1 :(得分:3)
我在联系人signal (7)
中找到了相关内容:
实时信号以保证的顺序发送。多 按顺序传送相同类型的实时信号 他们被送了。如果向a发送不同的实时信号 过程中,它们从最低编号开始交付 信号。 (即,低编号信号具有最高优先级。) 对比,如果有多个标准信号待处理, 它们的交付顺序未指定。
除了sigprocmask
之外,查看sigpending
和signal (7)
文档可以增强您对待处理信号保证的理解。
要从弱的“未指定”保证转移到您的OpenSUSE版本上实际发生的事情,您可能需要检查内核中的信号传递代码。