繁体   English   中英

Linux如何优先考虑自定义信号处理程序?

[英]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");
}

所以,在运行这段代码时,我期待这样的事情:

  1. 运行代码.... ^ C.
  2. 输入inthandler,执行循环,点击^ \\
  3. 退出inthandler,进入quithandler,执行quithandler循环
  4. ^ C回到inthandler。 如果我在inthandler中再次执行^ C,则忽略连续的inthandler信号,直到当前的inhanndler完成处理。

我发现基于观察的东西,似乎是信号的嵌套,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

换句话说,它似乎是这样处理的:

  1. 接收第一个^ C信号
  2. 接收^ \\信号,“延迟”inthandler并进入quithandler
  3. 接收下一个^ C信号,但因为我们已经在inthandler中“嵌套”了,所以把它放在inthandler“队列”的后面
  4. 接收quithandler,放在quithandler队列的后面。
  5. 执行quithandler直到queue为空。 忽略第三个quithandler,因为它似乎只有2的队列深度。
  6. 离开quithandler,然后执行剩余的2个inthandler。 忽略最终的inhanndler,因为队列深度为2。

我向我的教授展示了这种行为,他似乎同意“嵌套2队列深度”行为正在发生的事情,但我们并不是100%肯定为什么(他来自硬件背景,并且刚刚开始教授这门课程) )。 我想发布SO以了解是否有人可以阐明Linux处理这些信号的原因和方式,因为我们并不期待某些行为即嵌套。

我认为我写的测试用例应该足以说明发生了什么,但是这里有一些额外测试用例的截图:

http://imgur.com/Vya7JeY,fjfmrjd,30YRQfk,uHHXFu5,Pj35NbF

我想将其他测试用例留作链接,因为它们是一种大型屏幕截图。

谢谢!

规则(对于非实时信号,例如您正在使用的SIGQUITSIGINT )是:

  1. 默认情况下,在输入处理程序时屏蔽信号,在处理程序退出时屏蔽信号;
  2. 如果信号在被屏蔽时被引发,则它将保持挂起状态 ,并且如果/当该信号未被屏蔽时将被传送。

挂起状态是二进制 - 信号处于挂起或未处理状态。 如果在屏蔽的情况下多次引发信号,则在取消屏蔽时仍然只会传递一次。

那么你的例子中发生的是:

  1. 引发SIGINT并且inthandler()信号处理程序开始执行,并且SIGINT被屏蔽。
  2. 引发SIGQUIT ,并且quithandler()信号处理程序开始执行(使用SIGQUIT屏蔽中断inthandler() )。
  3. 引发SIGINT ,将SIGINT添加到待处理信号集(因为它被屏蔽)。
  4. 引发SIGQUIT ,将SIGQUIT添加到待处理信号集(因为它被屏蔽)。
  5. 引发了SIGQUIT ,但没有任何反应因为SIGQUIT已经挂起了。
  6. 引发了SIGINT ,但没有任何反应,因为SIGINT已经挂起。
  7. quithandler()完成执行, SIGQUIT被取消屏蔽。 因为SIGQUIT quithandler()挂起状态,所以它会被传递,并且quithandler()再次开始执行(再次屏蔽SIGQUIT )。
  8. quithandler()第二次完成执行, SIGQUIT被取消屏蔽。 SIGQUIT没有挂起,所以inthandler()然后继续执行( SIGINT仍然被屏蔽)。
  9. inthandler()完成执行,并且SIGINT未被屏蔽。 因为SIGINT inthandler()挂起状态,所以它会被传递,并且inthandler()再次开始执行(再次屏蔽SIGINT )。
  10. inthandler()第二次完成执行,并且SIGINT被取消屏蔽。 然后主要功能继续执行。

在Linux上,您可以通过检查/proc/<PID>/status来查看进程的当前屏蔽和挂起信号集。 掩蔽信号被显示在SigBlk:位掩码,并在未决的信号SigPnd:位掩码。

如果使用sigaction()而不是signal()安装信号处理程序,则可以指定SA_NODEFER标志,以请求在处理程序执行时不屏蔽信号。 您可以在程序中尝试这个 - 使用一个或两个信号 - 并尝试预测输出的样子。

我在联机帮助signal (7)发现了这一点似乎相关:

实时信号以保证顺序传送。 相同类型的多个实时信号按发送顺序传送。 如果向进程发送不同的实时信号,则从编号最小的信号开始传送它们。 (即,低编号信号具有最高优先级。)相反, 如果多个标准信号等待进程,则未指定它们的传递顺序。

除了signal (7)之外,查看sigprocmasksigpending文档应该可以增强您对待处理信号保证的理解。

要从弱的“未指定”保证转移到您的OpenSUSE版本上实际发生的事情,您可能需要检查内核中的信号传递代码。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM