私はsigaction()
で大量のシグナル(different signums)を処理するツールに取り組んでいます。
前のシグナルがシグナルハンドラーにあったときに、新しいシグナルが来る場合は、ケースを処理する必要があります。したがって、次の「スタック」を処理できる必要があります。
(シグナルハンドラーは独自のコンテキストで実行されるため、Afaikには実際のスタックはありませんが、これが私の質問を説明する方法です。)
Glibc2APIを使用しています。
問題は絶望的ではありません(メインの実行フローから、後で処理されるメインプロセスのリエントラントデータ構造に信号情報を渡すことができます)が、ハンドラーから見つけるための信頼できる方法が必要です。 「スタック」の最初かどうかです。
信号のマスキングは問題ありません。信号の損失を最小限に抑えること(つまり、コンフリクションによる)が最優先事項です。
信頼できる方法が必要です。グローバルsigatomic_tをスピンロックとして使用することも問題があります。signal1ハンドラーの開始直後(まだロックを取得しようとする前)に新しいシグナルが来ないことを保証できないためです。
マニュアルやglibcのドキュメントをたくさん調べた後、シグナルハンドラーが最初であるかどうかを確認するための、信頼できる方法が見つかりませんでした。どういうわけか可能ですか?
私の現在の最良のアイデア。少し考えが戻ってきましたが、うまくいくと確信しています。
秘訣は、_atomic_swap
_とグローバルポインタ(これを_SigAction* top
_と呼びます)です。 スピンロックとシグナルスタックの最後の要素へのポインタの両方として機能します。
したがって:* _top == NULL
_の場合、atomic_swap(top, myPointer)
はロックを取得します。 * _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;
}
}
_
シグナルハンドラーをシグナルをマスクするように構成し、このシグナルを受信したことを通知した後、マスクを解除します。
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 */
}
最後のpending_signals.empty()チェックとiretの間に小さな競合状態があることに注意してください。メインコードをチェックする場合は、シグナルの数が非常に多いことを考えると、とにかくすぐに処理される可能性があります。それ以外の場合は、最後に信号を再度クロックし、戻る前にpending_signalsがまだ空であることを確認できます。
別のアプローチは次のとおりです。
多くの異なる信号を受信する可能性がありますが、信号のセットには有限があります。また、到着した注文についてはあまり気にしないので、受信した信号の数を簡単に数えることができます。
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
プレフィックスが必要になる場合があります。