私は多数の信号を処理するツールを開発しています。その他記号)と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;
}
}