当信号处理程序执行时,新信号到达,如何决定哪个先执行?

当信号处理程序执行时,新信号到达,如何决定哪个先执行?

我正在开发一种处理大量信号的工具(带有不同的符号)与sigaction().

如果有新信号到来,而前一个信号位于信号处理程序中,我需要处理这种情况。因此,我需要能够处理以下“堆栈”:

  1. 正常流程
  2. 的处理程序信号1
  3. 的处理程序信号2
  4. ...可能还有更多信号处理程序...

(据我所知,没有真正的堆栈,因为信号处理程序在自己的上下文中运行,但这就是我可以说明我的问题的方式。)

我正在使用 glibc2 api。

问题并非没有希望(我可以将信号信息传递给主进程中的可重入数据结构,以便稍后从主执行流程中处理),但是我需要一种可靠的方法来从处理程序中找出它是否是“堆栈”上的第一个。

屏蔽信号是不行的,最大限度地减少信号损失(来自合并的信号)是首要任务。

我需要可靠的方式。使用全局 sigatomic_t 作为自旋锁也是有问题的,因为我不能保证新信号不会在开始后立即出现信号1处理程序(在尝试获取锁之前)。

在深入研究手册和 glibc 文档后,我没有找到任何可靠的方法来让信号处理程序确定他是否是第一个。这有可能吗?

答案1

将信号处理程序配置为屏蔽信号,然后在注释您收到此信号后取消屏蔽。

volatile_t sig_atomic_t signal_count;
void mysignalhandler(int signo) {
   sig_atomic_t depth = ++signal_count;
   pending_signals.push(signo)
   if (depth > 1) return;
   sigprocmask(<unblock all signals>)

    while (!pending_signals.empty())
       /* Process pending_signals */
}

请注意,最后一次挂起_signals.empty() 检查和 iret 之间存在一个小的竞争条件。如果您要对主代码进行一些检查,我会保留原样,考虑到您的信号数量庞大,无论如何它可能很快就会得到处理。否则,您可以在最后再次对信号进行计时,并在返回之前检查挂起的信号是否仍为空。

答案2

这是一种不同的方法:

您可能会收到许多不同的信号,但信号的集合是有限的。另外,我们不太关心它们到达的顺序,因此我们可以简单地计算收到的信号数量:

long signals[SIGRTMAX];
int signal_handler(int signum) {
    signals[argc]++;

    /* Locklessly process the contents of signals */
}

gcc -O1 -masm=intel将其转换为单个指令:

add QWORD PTR signals[0+rdi*8], 1

尽管它可能需要一个LOCK前缀,以防有多个核心和线程。

答案3

我目前最好的想法。一些想法还没有回来,但我相信它会起作用。

诀窍是:atomic_swap和一个全局指针(称之为SigAction* top)。它充当既是自旋锁又是指向信号堆栈最后一个元素的指针。

因此: * if top == NULL,atomic_swap(top, myPointer)获取锁。 * if top != NULLatomic_swap(top, myPointer)将 myPointer 放入栈顶,而 myPointer 将具有堆栈的前一个顶部元素。 * 堆栈是一个链表,每个链表都myPointer->next包含其下一个元素。

SigAction* top = NULL;

void handler(SigAction* action) {
  SigAction bkp;

  restart:

  bkp = action;
  atomic_swap(&top, &action);
  if (!action) { // we acquired the lock
    run_handler(action);
    atomic_swap(&top, &action); // release the lock
    if (action!= bkp) { // if there is a new element in the stack
      action = bkp->next;
      goto restart;
    }
  } else { // the lock is not ours
    action->next = bkp;
  }
}

相关内容