C中的信号处理 - 中断中的中断

Posted

技术标签:

【中文标题】C中的信号处理 - 中断中的中断【英文标题】:Signal handling in C - interrupt in interrupt 【发布时间】:2012-04-10 17:56:21 【问题描述】:

我想知道当我的程序同时处理其他信号时是否有可能被信号中断,我尝试用以下方式模拟它:

#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>

void sig_output()

    sigset_t set;
    sigprocmask(0,NULL,&set);
    printf("currently blocking:");
    if (sigismember(&set,SIGUSR1))
        printf("\nSIGUSR1");
    if(sigismember(&set,SIGUSR2))
        printf("\nSIGUSR2");
    printf("\n");
    return ;


void sig_handler(int sig)

    raise(SIGUSR1);    
    printf("start\n");
    if (sig==SIGUSR1)
        printf("SIGUSR1\n");
    else if (sig==SIGUSR2)
        printf("SIGUSR2\n");
    printf("end\n");
    return ;


void other_sig_handler(int sig)
  
    printf("start - other\n");
    if (sig==SIGUSR1)
        printf("SIGUSR1\n");
    else if (sig==SIGUSR2)
        printf("SIGUSR2\n");
    printf("end - other\n");
    return ;


int main()

    sig_output();
    struct sigaction a;
    a.sa_handler=sig_handler;
    a.sa_flags=0;
    sigset_t set,old;
    //blocking SIGUSR1,SIGUSR2
    sigemptyset(&set);
    sigaddset(&set,SIGUSR1);
    sigaddset(&set,SIGUSR2);
    printf("blocking SIGUSR1, SIGUSR2\n");
    sigprocmask(SIG_SETMASK,&set,&old);
    sig_output();
    //adding handles for SIGUSR1,SIGUSR2
    sigemptyset(&(a.sa_mask));
    sigaction(SIGUSR1,&a,NULL);
    a.sa_handler=other_sig_handler;
    sigaction(SIGUSR2,&a,NULL);
    printf("poczatek wysylania \n");
    raise(SIGUSR1);
    raise(SIGUSR2);
    raise(SIGUSR1);
    printf("using sigsuspend\n");
    sigsuspend(&old);
    printf("end of program\n");
    return 0;

每次我运行这个程序我都会得到

currently blocking:
blocking SIGUSR1, SIGUSR2
currently blocking:
SIGUSR1
SIGUSR2
raising
using sigsuspend
start - other
SIGUSR2
end - other
start
SIGUSR1
end
end of program

总是这样吗?

【问题讨论】:

这真的取决于处理器和操作系统。通常,许多 CPU 支持不同优先级的中断,这样优先级较高的中断可以在运行时跳入优先级较低的中断。 POSIX 信号不是硬件中断,因此处理器本身不会在这里受到质疑。 【参考方案1】:

引用sigaction(2) 联机帮助页:

信号例程通常使用导致其 调用被阻塞,但可能还会出现其他信号。全局信号掩码 定义当前阻止传递给进程的一组信号。 进程的信号掩码从其父进程的信号掩码初始化 (通常为空)。可以通过sigprocmask(2) 呼叫更改它,或者当 一个信号被传递给进程。

您可以使用SA_NODEFER 标志控制信号是否在其信号处理程序中自动阻塞。

【讨论】:

好吧,我一定错过了那一点信息。 还值得注意的是(我认为从文档中并不清楚,尽管它很合乎逻辑)您提供给sigactionsa_mask 是一个掩码附加 信号来阻止,而不是设置新的掩码。因此,如果您 (1) 要求使用显式阻止信号 Y 的 sa_mask 捕获信号 X(并隐式阻止 X,因为您将 SA_NODEFER 关闭),那么 (2) 使用 sigprocmask 阻止信号 Z,然后 ( 3) 实际上捕获一个信号 X,您的处理程序在所有 X、Y 和 Z 阻塞的情况下运行。 从 Linux 文档中可能不清楚; OS X(通过 FreeBSD)文档非常清楚新掩码是如何形成的。【参考方案2】:

据我所知,这些特定未决信号的传递顺序尚未定义。但是,信号(大多数情况下;SIGCLD 有一个例外,传统上是通过“作弊”来完成的)“非排队”,实时信号除外。非排队方面意味着如果您将信号 X 阻塞,然后 raise 它两次(就像您在上面对 SIGUSR1 所做的那样),您只会收到一次。

在至少一个系统 (MacOS) 上记录的唯一顺序是:

If multiple signals are ready to be delivered at the same time, any signals that
could be caused by traps are delivered first.

(这些是SIGSEGVSIGBUS 之类的东西。)通常,您可以通过使用信号阻塞掩码来控制传递顺序:在某个时刻取消阻塞任何特定信号,这些就是那些可以在那个时候交付。

如果您不设置SA_NODEFER,则处理程序入口处的阻塞掩码将始终阻止处理程序正在处理的任何信号,因此您不必担心递归。

SIGCLD 的特殊情况来自 System V,它最初通过在每次 SIGCLD 交付时将处理程序重置为 SIG_DFL 来实现这一点。 (事实上​​,SysV 对所有信号都这样做了,有效地实现了SA_RESETHAND,无论你是否想要它。)默认操作是丢弃信号,就好像处理程序是SIG_IGN。当多个子进程在处理程序执行其操作之前完成时,这当然会产生竞争条件。但是,SysV 人员并没有使用阻塞/解除阻塞模型,而是进行了修改:在您的SIGCLD 处理程序的末尾,您将调用signal(SIGCLD, handler); 来修复处理程序。那时,如果有任何已退出的子代尚未被wait-ed 处理,SysV 将立即生成一个 new SIGCLD,并且您的处理程序将被递归输入。这使得信号看起来像是在排队,但实际上并没有排队。

有关 Linux 信号的更多信息,请参阅(例如)http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html

【讨论】:

以上是关于C中的信号处理 - 中断中的中断的主要内容,如果未能解决你的问题,请参考以下文章

Linux驱动实践:中断处理中的工作队列 workqueue 是什么鬼?

C语言中的interrupt是怎么回事

Windows中的中断处理

请教高人C51单片机C语言中的中断优先级怎么设置?

Unix环境_信号处理机制

zynq7000 中断系统及在UCOSIII中的中断处理接口