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

时间:2015-04-20 03:41:02

标签: c linux signals

我们上周进行了一次演讲,其中涉及操作系统(在本例中为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 loop
  4. ^ C回到inthandler。如果我在inthandler中再次执行^ C,则忽略连续的inthandler信号,直到当前的inhanndler完成处理。
  5. 我发现基于观察的东西,似乎是一个嵌套的,2队列深度的&#34;调度&#34;信号。例如,如果我快速连续输入以下中断:

    • ^ 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. 接收^ \信号,&#34;延迟&#34; inhandler并进入quithandler
    3. 接收下一个^ C信号,但因为我们是#34;嵌套&#34;已经在inthandler中,把它放在inhanndler&#34;队列的后面&#34;
    4. 接收quithandler,放在quithandler队列的后面。
    5. 执行quithandler,直到队列为空。忽略第三个quithandler,因为它似乎只有2的队列深度。
    6. 离开quithandler,并执行剩余的2个inthandler。忽略最终的inhanndler,因为队列深度为2。
    7. 我向我的教授展示了这种行为,他似乎同意&#34;嵌套2队列深度&#34;行为是正在发生的事情,但我们并不是100%肯定为什么(他来自硬件背景,而且刚刚开始教授这门课程)。我想发布SO以了解是否有人可以阐明Linux处理这些信号的原因和方式,因为我们并不期待某些行为,即嵌套。

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

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

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

      谢谢!

2 个答案:

答案 0 :(得分:9)

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

  1. 默认情况下,输入处理程序时会屏蔽信号,处理程序退出时会屏蔽信号;
  2. 如果信号在被屏蔽时被引发,则它将挂起,并且如果/当该信号被取消屏蔽时将被传送。
  3. 待处理状态为二进制 - 信号处于暂挂或未处理状态。如果在屏蔽的情况下多次引发信号,则在取消屏蔽时仍然只会传递一次。

    所以你的例子中会发生什么:

    1. SIGINT被引发,inthandler()信号处理程序开始执行,SIGINT被屏蔽。
    2. 引发了
    3. SIGQUITquithandler()信号处理程序开始执行(中断inthandler())并屏蔽了SIGQUIT
    4. 引发了
    5. SIGINT,将SIGINT添加到待处理信号集中(因为它已被屏蔽)。
    6. 引发
    7. SIGQUIT,将SIGQUIT添加到待处理信号集(因为它被屏蔽)。
    8. 引发了
    9. SIGQUIT,但没有任何结果,因为SIGQUIT已经处于待处理状态。
    10. 引发了
    11. SIGINT,但没有任何结果,因为SIGINT已经处于待处理状态。
    12. quithandler()完成执行,SIGQUIT被取消屏蔽。由于SIGQUIT处于待处理状态,因此会传递,quithandler()再次开始执行(再次屏蔽SIGQUIT)。
    13. quithandler()第二次完成执行,SIGQUIT被取消屏蔽。 SIGQUIT未处于待处理状态,因此inthandler()会继续执行(SIGINT仍然屏蔽)。
    14. inthandler()完成执行,SIGINT被取消屏蔽。由于SIGINT处于待处理状态,因此会传递,inthandler()再次开始执行(再次屏蔽SIGINT)。
    15. inthandler()第二次完成执行,SIGINT被取消屏蔽。然后主要功能继续执行。
    16. 在Linux上,您可以通过检查/proc/<PID>/status来查看进程的当前屏蔽和待处理信号集。屏蔽信号显示在SigBlk:位掩码和SigPnd:位掩码中的待处理信号中。

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

答案 1 :(得分:3)

我在联系人signal (7)中找到了相关内容:

  

实时信号以保证的顺序发送。多          按顺序传送相同类型的实时信号          他们被送了。如果向a发送不同的实时信号          过程中,它们从最低编号开始交付          信号。 (即,低编号信号具有最高优先级。)          对比,如果有多个标准信号待处理,          它们的交付顺序未指定。

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

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