[英]How does Linux prioritize custom signal handlers?
我们上周进行了一次演讲,其中涉及操作系统(在本例中为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队列深度“调度”。 例如,如果我快速连续输入以下中断:
我将从代码中收到以下行为/输出:
^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
换句话说,它似乎是这样处理的:
我向我的教授展示了这种行为,他似乎同意“嵌套2队列深度”行为正在发生的事情,但我们并不是100%肯定为什么(他来自硬件背景,并且刚刚开始教授这门课程) )。 我想发布SO以了解是否有人可以阐明Linux处理这些信号的原因和方式,因为我们并不期待某些行为即嵌套。
我认为我写的测试用例应该足以说明发生了什么,但是这里有一些额外测试用例的截图:
http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF
我想将其他测试用例留作链接,因为它们是一种大型屏幕截图。
谢谢!
规则(对于非实时信号,例如您正在使用的SIGQUIT
和SIGINT
)是:
挂起状态是二进制 - 信号处于挂起或未处理状态。 如果在屏蔽的情况下多次引发信号,则在取消屏蔽时仍然只会传递一次。
那么你的例子中发生的是:
SIGINT
并且inthandler()
信号处理程序开始执行,并且SIGINT
被屏蔽。 SIGQUIT
,并且quithandler()
信号处理程序开始执行(使用SIGQUIT
屏蔽中断inthandler()
)。 SIGINT
,将SIGINT
添加到待处理信号集(因为它被屏蔽)。 SIGQUIT
,将SIGQUIT
添加到待处理信号集(因为它被屏蔽)。 SIGQUIT
,但没有任何反应因为SIGQUIT
已经挂起了。 SIGINT
,但没有任何反应,因为SIGINT
已经挂起。 quithandler()
完成执行, SIGQUIT
被取消屏蔽。 因为SIGQUIT
quithandler()
挂起状态,所以它会被传递,并且quithandler()
再次开始执行(再次屏蔽SIGQUIT
)。 quithandler()
第二次完成执行, SIGQUIT
被取消屏蔽。 SIGQUIT
没有挂起,所以inthandler()
然后继续执行( SIGINT
仍然被屏蔽)。 inthandler()
完成执行,并且SIGINT
未被屏蔽。 因为SIGINT
inthandler()
挂起状态,所以它会被传递,并且inthandler()
再次开始执行(再次屏蔽SIGINT
)。 inthandler()
第二次完成执行,并且SIGINT
被取消屏蔽。 然后主要功能继续执行。 在Linux上,您可以通过检查/proc/<PID>/status
来查看进程的当前屏蔽和挂起信号集。 掩蔽信号被显示在SigBlk:
位掩码,并在未决的信号SigPnd:
位掩码。
如果使用sigaction()
而不是signal()
安装信号处理程序,则可以指定SA_NODEFER
标志,以请求在处理程序执行时不屏蔽信号。 您可以在程序中尝试这个 - 使用一个或两个信号 - 并尝试预测输出的样子。
我在联机帮助signal (7)
发现了这一点似乎相关:
实时信号以保证顺序传送。 相同类型的多个实时信号按发送顺序传送。 如果向进程发送不同的实时信号,则从编号最小的信号开始传送它们。 (即,低编号信号具有最高优先级。)相反, 如果多个标准信号等待进程,则未指定它们的传递顺序。
除了signal (7)
之外,查看sigprocmask
和sigpending
文档应该可以增强您对待处理信号保证的理解。
要从弱的“未指定”保证转移到您的OpenSUSE版本上实际发生的事情,您可能需要检查内核中的信号传递代码。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.