我正在开发一种处理大量信号的工具(带有不同的符号)与sigaction()
.
如果有新信号到来,而前一个信号位于信号处理程序中,我需要处理这种情况。因此,我需要能够处理以下“堆栈”:
- 正常流程
- 的处理程序信号1
- 的处理程序信号2
- ...可能还有更多信号处理程序...
(据我所知,没有真正的堆栈,因为信号处理程序在自己的上下文中运行,但这就是我可以说明我的问题的方式。)
我正在使用 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 != NULL
,atomic_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;
}
}