web-dev-qa-db-ja.com

実行がシグナルハンドラーで行われている間に新しいシグナルが到着します。どちらが最初かを判断するにはどうすればよいですか?

私はsigaction()で大量のシグナル(different signums)を処理​​するツールに取り組んでいます。

前のシグナルがシグナルハンドラーにあったときに、新しいシグナルが来る場合は、ケースを処理する必要があります。したがって、次の「スタック」を処理できる必要があります。

  1. プロセスの通常の流れ
  2. signal1のハンドラー
  3. signal2のハンドラー
  4. ...おそらくさらにシグナルハンドラー...

(シグナルハンドラーは独自のコンテキストで実行されるため、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がまだ空であることを確認できます。

2
Ángel

別のアプローチは次のとおりです。

多くの異なる信号を受信する可能性がありますが、信号のセットには有限があります。また、到着した注文についてはあまり気にしないので、受信した信号の数を簡単に数えることができます。

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プレフィックスが必要になる場合があります。

1
Ángel